Skip to content

Commit 0c44117

Browse files
committed
feat(core): add template rendering with res.render and view integration
1 parent 1de9c29 commit 0c44117

7 files changed

Lines changed: 333 additions & 19 deletions

File tree

include/vix/app/App.hpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
#include <vix/server/HTTPServer.hpp>
3535
#include <vix/utils/Logger.hpp>
3636
#include <vix/utils/ServerPrettyLogs.hpp>
37+
#include <vix/template/Engine.hpp>
38+
#include <vix/template/FileSystemLoader.hpp>
39+
#include <vix/view/TemplateView.hpp>
3740

3841
namespace vix::router
3942
{
@@ -324,6 +327,44 @@ namespace vix
324327
return *executor_;
325328
}
326329

330+
/**
331+
* @brief Configure the application template directory.
332+
*
333+
* This initializes:
334+
* - the file-system template loader
335+
* - the template engine
336+
* - the HTTP-oriented template view facade
337+
*
338+
* @param directory Root directory containing template files.
339+
* @return App& Current application instance.
340+
*/
341+
App &templates(const std::string &directory);
342+
343+
/**
344+
* @brief Returns whether template rendering is configured.
345+
*
346+
* @return true if views are available, false otherwise.
347+
*/
348+
[[nodiscard]] bool has_views() const noexcept;
349+
350+
/**
351+
* @brief Returns the template rendering facade.
352+
*
353+
* @return vix::view::TemplateView& Mutable template view facade.
354+
*
355+
* @throws std::runtime_error if templates() was not called.
356+
*/
357+
[[nodiscard]] vix::view::TemplateView &views();
358+
359+
/**
360+
* @brief Returns the template rendering facade.
361+
*
362+
* @return const vix::view::TemplateView& Immutable template view facade.
363+
*
364+
* @throws std::runtime_error if templates() was not called.
365+
*/
366+
[[nodiscard]] const vix::view::TemplateView &views() const;
367+
327368
/**
328369
* @brief Indicates whether the server has started.
329370
*
@@ -762,7 +803,10 @@ namespace vix
762803
};
763804

764805
using Adapter = vix::vhttp::RequestHandler<decltype(wrapped)>;
765-
auto request_handler = std::make_shared<Adapter>(path, std::move(wrapped));
806+
auto request_handler = std::make_shared<Adapter>(
807+
path,
808+
std::move(wrapped),
809+
template_view_.get());
766810

767811
router_->add_route(method, path, request_handler, opt);
768812

@@ -874,7 +918,10 @@ namespace vix
874918
};
875919

876920
using Adapter = vix::vhttp::RequestHandler<decltype(wrapped)>;
877-
auto request_handler = std::make_shared<Adapter>(path, std::move(wrapped));
921+
auto request_handler = std::make_shared<Adapter>(
922+
path,
923+
std::move(wrapped),
924+
template_view_.get());
878925

879926
router_->add_route("OPTIONS", path, request_handler, vix::router::RouteOptions{});
880927
}
@@ -905,6 +952,21 @@ namespace vix
905952

906953
std::vector<MiddlewareEntry> middlewares_;
907954
std::atomic<bool> closed_{false};
955+
956+
/**
957+
* @brief Shared template engine used by the application runtime.
958+
*/
959+
std::shared_ptr<vix::template_::Engine> template_engine_{};
960+
961+
/**
962+
* @brief HTTP-oriented template rendering facade.
963+
*/
964+
std::unique_ptr<vix::view::TemplateView> template_view_{};
965+
966+
/**
967+
* @brief Configured template root directory.
968+
*/
969+
std::string templates_directory_{};
908970
};
909971

910972
} // namespace vix

include/vix/core.hpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* Copyright 2025, Gaspard Kirira. All rights reserved.
77
* https://github.com/vixcpp/vix
8-
* Use of this source code is governed by a MIT license that can be found in the License file.
8+
* Use of this source code is governed by a MIT license thatcan be found in the License file.
99
*
1010
* Vix.cpp
1111
*
@@ -18,9 +18,6 @@
1818
#include <vector>
1919
#include <functional>
2020

21-
#include <boost/asio.hpp>
22-
#include <boost/beast/http.hpp>
23-
2421
#include <vix/console.hpp>
2522
#include <vix/print.hpp>
2623
#include <vix/console.hpp>
@@ -35,17 +32,22 @@
3532
#include <vix/config/Config.hpp>
3633
#include <vix/http/Status.hpp>
3734

38-
namespace asio = boost::asio;
39-
namespace beast = boost::beast;
40-
namespace bhttp = boost::beast::http;
41-
4235
namespace vix
4336
{
44-
4537
/**
46-
* @brief Core type aliases and common includes for Vix.
38+
* @brief Public template namespace alias.
39+
*
40+
* This alias exposes the template module under a shorter and cleaner name.
41+
* It allows users to access template-related types such as Context and Engine
42+
* without referencing the internal `template_` namespace directly.
43+
*
44+
* Example:
45+
* @code
46+
* vix::tmpl::Context ctx;
47+
* ctx.set("title", "Home");
48+
* @endcode
4749
*/
48-
using tcp = asio::ip::tcp;
50+
namespace tmpl = vix::template_;
4951

5052
/** @brief Shared pointer to App. */
5153
using AppPtr = std::shared_ptr<App>;
@@ -58,7 +60,6 @@ namespace vix
5860

5961
/** @brief HTTP response alias. */
6062
using Response = vix::vhttp::ResponseWrapper;
61-
6263
} // namespace vix
6364

6465
#endif // VIX_CORE_HPP

include/vix/http/RequestHandler.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,17 +221,20 @@ namespace vix::vhttp
221221

222222
public:
223223
/** @brief Create a handler adapter for a route pattern and a user handler. */
224-
RequestHandler(std::string route_pattern, Handler handler)
224+
RequestHandler(std::string route_pattern,
225+
Handler handler,
226+
vix::view::TemplateView *template_view = nullptr)
225227
: route_pattern_(std::move(route_pattern)),
226-
handler_(std::move(handler))
228+
handler_(std::move(handler)),
229+
template_view_(template_view)
227230
{
228231
static_assert(sizeof(Handler) > 0, "Handler type must be complete here.");
229232
}
230233

231234
/** @brief Execute the user handler and write the final HTTP response. */
232235
task<void> handle_request(const Request &incoming_req, Response &res) override
233236
{
234-
ResponseWrapper wrapped{res};
237+
ResponseWrapper wrapped{res, template_view_};
235238

236239
auto params = extract_params_from_path(route_pattern_, incoming_req.path());
237240
auto state = incoming_req.state_ptr()
@@ -318,6 +321,7 @@ namespace vix::vhttp
318321
private:
319322
std::string route_pattern_;
320323
Handler handler_;
324+
vix::view::TemplateView *template_view_{nullptr};
321325

322326
static void finalize_response(const Request &req, Response &res)
323327
{

include/vix/http/ResponseWrapper.hpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <vix/http/Response.hpp>
3131
#include <vix/http/Status.hpp>
3232
#include <vix/json/Simple.hpp>
33+
#include <vix/template/Context.hpp>
34+
#include <vix/view/TemplateView.hpp>
3335

3436
namespace vix::vhttp
3537
{
@@ -130,10 +132,14 @@ namespace vix::vhttp
130132
struct ResponseWrapper
131133
{
132134
Response &res;
135+
vix::view::TemplateView *template_view_{nullptr};
133136

134137
/** @brief Wrap an existing native Vix response and ensure a default status code. */
135-
explicit ResponseWrapper(Response &r) noexcept
136-
: res(r)
138+
explicit ResponseWrapper(
139+
Response &r,
140+
vix::view::TemplateView *template_view = nullptr) noexcept
141+
: res(r),
142+
template_view_(template_view)
137143
{
138144
if (!is_valid_status(res.status()))
139145
res.set_status(OK);
@@ -396,6 +402,32 @@ namespace vix::vhttp
396402
return *this;
397403
}
398404

405+
/** @brief Render an HTML template using the configured template view. */
406+
ResponseWrapper &render(
407+
const std::string &name,
408+
const vix::template_::Context &context)
409+
{
410+
ensure_status();
411+
412+
const int s = res.status();
413+
if (s == NO_CONTENT || s == NOT_MODIFIED)
414+
return this->send();
415+
416+
if (!template_view_)
417+
{
418+
throw std::runtime_error(
419+
"ResponseWrapper::render() called but templates are not configured");
420+
}
421+
422+
const int current_status = res.status();
423+
424+
auto rendered = template_view_->render_response(name, context);
425+
rendered.set_status(current_status);
426+
427+
res = std::move(rendered);
428+
return *this;
429+
}
430+
399431
/** @brief Send JSON using nlohmann::json with an auto Content-Type if missing. */
400432
ResponseWrapper &json(const nlohmann::json &j)
401433
{

include/vix/view/TemplateView.hpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
*
3+
* @file TemplateView.hpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2026, Gaspard Kirira.
7+
* All rights reserved.
8+
* https://github.com/vixcpp/vix
9+
*
10+
* Use of this source code is governed by a MIT license
11+
* that can be found in the License file.
12+
*
13+
* Vix.cpp
14+
*
15+
*/
16+
17+
#ifndef VIX_VIEW_TEMPLATE_VIEW_HPP
18+
#define VIX_VIEW_TEMPLATE_VIEW_HPP
19+
20+
#include <memory>
21+
#include <string>
22+
23+
#include <vix/http/Response.hpp>
24+
#include <vix/template/Context.hpp>
25+
#include <vix/template/Engine.hpp>
26+
27+
namespace vix::view
28+
{
29+
/**
30+
* @brief HTTP-integrated template rendering facade.
31+
*
32+
* TemplateView is the bridge between:
33+
* - the Vix runtime HTTP layer
34+
* - the template engine
35+
*
36+
* Responsibilities:
37+
* - render templates by name
38+
* - convert render results into native HTTP responses
39+
* - expose streaming rendering for large outputs
40+
*/
41+
class TemplateView
42+
{
43+
public:
44+
/**
45+
* @brief Construct a TemplateView from a template engine.
46+
*
47+
* @param engine Shared template engine instance.
48+
*/
49+
explicit TemplateView(
50+
std::shared_ptr<vix::template_::Engine> engine);
51+
52+
/**
53+
* @brief Render a template to a string.
54+
*
55+
* @param name Template name.
56+
* @param context Runtime rendering context.
57+
* @return Rendered HTML string.
58+
*/
59+
[[nodiscard]] std::string render(
60+
const std::string &name,
61+
const vix::template_::Context &context) const;
62+
63+
/**
64+
* @brief Render a template and produce a native HTTP response.
65+
*
66+
* @param name Template name.
67+
* @param context Runtime rendering context.
68+
* @return HTTP response containing rendered HTML.
69+
*/
70+
[[nodiscard]] vix::vhttp::Response render_response(
71+
const std::string &name,
72+
const vix::template_::Context &context) const;
73+
74+
/**
75+
* @brief Render a template in streaming mode.
76+
*
77+
* @tparam Output Output sink type.
78+
* @param name Template name.
79+
* @param context Runtime rendering context.
80+
* @param out Output sink receiving rendered chunks.
81+
*/
82+
template <typename Output>
83+
void render_stream(
84+
const std::string &name,
85+
const vix::template_::Context &context,
86+
Output &out) const
87+
{
88+
engine_->render_stream(name, context, out);
89+
}
90+
91+
/**
92+
* @brief Access the underlying template engine.
93+
*
94+
* @return Shared engine instance.
95+
*/
96+
[[nodiscard]] const std::shared_ptr<vix::template_::Engine> &
97+
engine() const noexcept;
98+
99+
private:
100+
/**
101+
* @brief Shared template engine.
102+
*/
103+
std::shared_ptr<vix::template_::Engine> engine_;
104+
};
105+
106+
} // namespace vix::view
107+
108+
#endif // VIX_VIEW_TEMPLATE_VIEW_HPP

0 commit comments

Comments
 (0)