1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! Step-by-step tutorial: build a minimal plugin that renders "Hello, World!".
//!
//! # Creating a Hello World plugin
//!
//! Follow these steps to define a plugin, register a route and page template, write a
//! handler, and boot the server.
//!
//! # Step 1: Create the plugin entrypoint (`mod.rs`)
//!
//! Every plugin needs a zero-sized tag struct and an `install` function generated by
//! [`define_plugin_install!`](crate::plugin_install::define_plugin_install):
//!
//! ```ignore
//! // src/plugins/hello/mod.rs
//! pub mod handlers;
//! pub mod routes;
//! pub mod templates;
//!
//! use lariv_rs::plugin_install::define_plugin_install;
//!
//! /// Plugin identity tag.
//! pub struct HelloTag;
//!
//! define_plugin_install! {
//! plugin: HelloTag;
//! /// Register hello templates and HTTP routes.
//! steps: [
//! templates(templates::Hook),
//! http(routes::Hook),
//! ]
//! }
//! ```
//!
//! # Step 2: Add HTTP routing (`routes.rs`)
//!
//! Routes are declared with [`define_plugin_routes!`](crate::define_plugin_routes).
//! Each line maps a path pattern to an async handler function:
//!
//! ```ignore
//! // src/plugins/hello/routes.rs
//! use lariv_rs::define_plugin_routes;
//! use super::handlers;
//!
//! define_plugin_routes! {
//! plugin: HelloTag;
//! routes: [
//! get HelloRouteTag, "/hello", handlers::hello;
//! ]
//! }
//! ```
//!
//! The macro generates a typed route tag (`HelloRouteTag`) with `.url()` and `.path()`
//! helpers for use in templates and redirects.
//!
//! # Step 3: Create the page template (`templates.rs`)
//!
//! Pages are plain structs that implement [`RenderTemplate`](crate::template::RenderTemplate)
//! and render Maud markup. Register them with [`define_register_items!`](crate::capability::define_register_items):
//!
//! ```ignore
//! // src/plugins/hello/templates.rs
//! use maud::{Markup, html};
//! use lariv_rs::{
//! capability::define_register_items,
//! components::ShellChrome,
//! template::{RenderTemplate, TemplateCapability, TemplateOf, TemplateRegistrar},
//! };
//! use super::HelloTag;
//!
//! pub struct HelloPageTag;
//! pub struct HelloPage;
//!
//! impl RenderTemplate for HelloPage {
//! fn render(&self, _chrome: &ShellChrome) -> Markup {
//! html! {
//! div class="container mx-auto p-8" {
//! h1 class="text-2xl font-bold" { "Hello, World!" }
//! }
//! }
//! }
//! }
//!
//! define_register_items! {
//! plugin: HelloTag;
//! capability: TemplateCapability;
//! trait: TemplateRegistrar;
//! method: register_templates;
//! wrapper: TemplateOf;
//! bounds: [Clone];
//! hook: Hook;
//! items: [
//! HelloIdx: HelloPageTag => HelloPage,
//! ]
//! }
//! ```
//!
//! # Step 4: Write the handler (`handlers.rs`)
//!
//! Handlers are async Axum functions. For a simple page, build the template struct and
//! return HTML via [`html_built_page_or_app_layout`](crate::web::html_built_page_or_app_layout):
//!
//! ```ignore
//! // src/plugins/hello/handlers.rs
//! use lariv_rs::{
//! components::{SharedChromeFolder, SlotCtx},
//! http::Cap,
//! web::{Htmx, html_built_page_or_app_layout},
//! };
//! use super::templates::HelloPage;
//!
//! pub async fn hello(
//! Cap(chrome): Cap<SharedChromeFolder>,
//! htmx: Htmx,
//! ) -> maud::Markup {
//! let page = HelloPage;
//! let slot_ctx = SlotCtx::default();
//! html_built_page_or_app_layout(&page, &htmx, &chrome, &slot_ctx)
//! }
//! ```
//!
//! # Step 5: Bootstrap the server (`main.rs`)
//!
//! Install your plugin on a web app, load config, mount, and run:
//!
//! ```ignore
//! // src/main.rs
//! use lariv_rs::app::App;
//!
//! mod plugins {
//! pub mod hello;
//! }
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let app = App::new_web_app();
//! let app = plugins::hello::install(app);
//! let app = app.load_config("config.toml").await?;
//! let mounted = app.mount();
//! mounted.run().await
//! }
//! ```
//!
//! Create a minimal `config.toml` at the project root (or rely on defaults):
//!
//! ```toml
//! database_url = "sqlite://data/lariv.db?mode=rwc"
//! bind = "127.0.0.1:3000"
//! ```
//!
//! Run the server:
//!
//! ```text
//! cargo run
//! ```
//!
//! Open [http://127.0.0.1:3000/hello](http://127.0.0.1:3000/hello) in your browser.
//!
//! # Next steps
//!
//! - Add a dashboard tile: see [`super::app`] and [`define_register_apps!`](crate::apps::define_register_apps)
//! - Share a capability across plugins: see [`super::app`] (`cap_attach` / `cap_hook`)
//! - Load database records: see [`super::layers`] and [`super::entities`]
//! - Add plugin configuration: see [`super::config`]
//! - Full project layout: see [`super`] module documentation