Skip to main content

server_less/
lib.rs

1//! Server-less - Composable derive macros for Rust
2//!
3//! Server-less takes an **impl-first** approach: write your Rust methods,
4//! and derive macros project them into various protocols (HTTP, CLI, MCP, WebSocket).
5//!
6//! # Quick Start
7//!
8//! ```no_run
9//! use server_less::prelude::*;
10//! use serde::{Deserialize, Serialize};
11//!
12//! #[derive(Serialize, Deserialize)]
13//! struct User { name: String, email: String }
14//!
15//! #[derive(Debug, ServerlessError)]
16//! enum UserError { NotFound }
17//!
18//! struct UserService;
19//!
20//! #[mcp]
21//! impl UserService {
22//!     /// Create a new user
23//!     async fn create_user(&self, name: String, email: String) -> Result<User, UserError> {
24//!         Ok(User { name, email })
25//!     }
26//!
27//!     /// List all users
28//!     async fn list_users(&self, limit: Option<u32>) -> Vec<User> {
29//!         let _ = limit;
30//!         vec![]
31//!     }
32//! }
33//! ```
34//!
35//! This generates:
36//! - **MCP**: Tools `create_user`, `list_users` (Model Context Protocol)
37//!
38//! # Available Macros
39//!
40//! | Macro | Protocol | Generated Methods |
41//! |-------|----------|-------------------|
42//! | `#[http]` | HTTP/REST | `http_router()`, `openapi_spec()` |
43//! | `#[cli]` | Command Line | `cli_command()`, `cli_run()` |
44//! | `#[mcp]` | MCP | `mcp_tools()`, `mcp_call()`, `mcp_call_async()` |
45//! | `#[ws]` | WebSocket | `ws_router()`, `ws_handle_message()`, `ws_handle_message_async()` |
46//!
47//! # Naming Conventions
48//!
49//! Method names infer HTTP methods and CLI subcommand structure:
50//!
51//! | Prefix | HTTP | CLI |
52//! |--------|------|-----|
53//! | `create_*`, `add_*` | POST | `<cmd> create-*` |
54//! | `get_*`, `fetch_*` | GET (single) | `<cmd> get-*` |
55//! | `list_*`, `find_*` | GET (collection) | `<cmd> list-*` |
56//! | `update_*`, `set_*` | PUT | `<cmd> update-*` |
57//! | `delete_*`, `remove_*` | DELETE | `<cmd> delete-*` |
58//!
59//! # Return Types
60//!
61//! | Type | HTTP | CLI | MCP/WS |
62//! |------|------|-----|--------|
63//! | `T` | 200 + JSON | stdout JSON | JSON result |
64//! | `Option<T>` | 200 or 404 | stdout or exit 1 | result or null |
65//! | `Result<T, E>` | 200 or error | stdout or stderr | result or error |
66//! | `()` | 204 | silent | `{"success": true}` |
67//! | `impl Stream<Item=T>` | SSE | N/A | N/A |
68//!
69//! # Async Methods
70//!
71//! All macros support async methods:
72//!
73//! ```no_run
74//! use server_less::prelude::*;
75//!
76//! struct MyService;
77//!
78//! #[mcp]
79//! impl MyService {
80//!     /// Sync method - works with mcp_call() and mcp_call_async()
81//!     pub fn sync_method(&self) -> String {
82//!         String::from("hello")
83//!     }
84//!
85//!     /// Async method - use mcp_call_async() for proper await
86//!     pub async fn async_method(&self) -> String {
87//!         String::from("hello async")
88//!     }
89//! }
90//!
91//! #[tokio::main]
92//! async fn main() {
93//!     let service = MyService;
94//!     // Sync call (errors on async methods)
95//!     service.mcp_call("sync_method", serde_json::json!({}));
96//!     // Async call (awaits async methods properly)
97//!     service.mcp_call_async("async_method", serde_json::json!({})).await;
98//! }
99//! ```
100//!
101//! # SSE Streaming (HTTP)
102//!
103//! Return `impl Stream<Item=T>` for Server-Sent Events:
104//!
105//! ```no_run
106//! use server_less::prelude::*;
107//! use serde::{Deserialize, Serialize};
108//!
109//! #[derive(Clone, Serialize, Deserialize)]
110//! struct Event { message: String }
111//!
112//! #[derive(Clone)]
113//! struct StreamService;
114//!
115//! #[http]
116//! impl StreamService {
117//!     // Note: Rust 2024 requires `+ use<>` to avoid lifetime capture
118//!     pub fn stream_events(&self) -> impl futures::Stream<Item = Event> + use<> {
119//!         futures::stream::iter(vec![Event { message: String::from("hello") }])
120//!     }
121//! }
122//! ```
123//!
124//! # Feature Flags
125//!
126//! Enable only what you need:
127//!
128//! ```toml
129//! [dependencies]
130//! server-less = { version = "0.2", default-features = false, features = ["http", "cli"] }
131//! ```
132//!
133//! Available features:
134//! - `mcp` - MCP macro (no extra deps)
135//! - `http` - HTTP macro (requires axum)
136//! - `cli` - CLI macro (requires clap)
137//! - `ws` - WebSocket macro (requires axum, futures)
138//! - `full` - All features (default)
139
140// Re-export macros (feature-gated)
141#[cfg(feature = "mcp")]
142pub use server_less_macros::mcp;
143
144#[cfg(feature = "http")]
145pub use server_less_macros::http;
146
147#[cfg(any(feature = "http", feature = "openapi"))]
148pub use server_less_macros::openapi;
149
150#[cfg(feature = "http")]
151pub use server_less_macros::route;
152
153#[cfg(feature = "http")]
154pub use server_less_macros::response;
155
156#[cfg(feature = "http")]
157pub use server_less_macros::serve;
158
159#[cfg(feature = "cli")]
160pub use server_less_macros::cli;
161
162#[cfg(feature = "cli")]
163pub use server_less_core::CliSubcommand;
164
165#[cfg(feature = "cli")]
166pub use server_less_core::cli_format_output;
167
168#[cfg(feature = "mcp")]
169pub use server_less_core::McpNamespace;
170
171#[cfg(feature = "jsonrpc")]
172pub use server_less_core::JsonRpcMount;
173
174#[cfg(feature = "ws")]
175pub use server_less_core::WsMount;
176
177#[cfg(feature = "http")]
178pub use server_less_core::HttpMount;
179
180#[cfg(feature = "ws")]
181pub use server_less_macros::ws;
182
183#[cfg(feature = "jsonrpc")]
184pub use server_less_macros::jsonrpc;
185
186#[cfg(feature = "openrpc")]
187pub use server_less_macros::openrpc;
188
189#[cfg(feature = "graphql")]
190pub use server_less_macros::graphql;
191#[cfg(feature = "graphql")]
192pub use server_less_macros::graphql_enum;
193#[cfg(feature = "graphql")]
194pub use server_less_macros::graphql_input;
195
196#[cfg(feature = "grpc")]
197pub use server_less_macros::grpc;
198
199#[cfg(feature = "capnp")]
200pub use server_less_macros::capnp;
201
202#[cfg(feature = "thrift")]
203pub use server_less_macros::thrift;
204
205#[cfg(feature = "connect")]
206pub use server_less_macros::connect;
207
208#[cfg(feature = "smithy")]
209pub use server_less_macros::smithy;
210
211#[cfg(feature = "markdown")]
212pub use server_less_macros::markdown;
213
214#[cfg(feature = "jsonschema")]
215pub use server_less_macros::jsonschema;
216
217#[cfg(feature = "asyncapi")]
218pub use server_less_macros::asyncapi;
219
220// Blessed presets
221#[cfg(feature = "http")]
222pub use server_less_macros::server;
223
224#[cfg(feature = "jsonrpc")]
225pub use server_less_macros::rpc;
226
227#[cfg(feature = "mcp")]
228pub use server_less_macros::tool;
229
230#[cfg(feature = "cli")]
231pub use server_less_macros::program;
232
233// Error derive macro (always available - no deps, commonly needed)
234pub use server_less_macros::ServerlessError;
235
236// Re-export futures for generated WebSocket code
237#[cfg(feature = "ws")]
238pub use futures;
239
240// Re-export async-graphql for generated GraphQL code
241#[cfg(feature = "graphql")]
242pub use async_graphql;
243#[cfg(feature = "graphql")]
244pub use async_graphql_axum;
245
246// Re-export core types
247pub use server_less_core::*;
248
249// Re-export OpenAPI composition utilities (available when any protocol that generates OpenAPI is enabled)
250#[cfg(feature = "server-less-openapi")]
251pub use server_less_openapi::{
252    OpenApiBuilder, OpenApiError, OpenApiOperation, OpenApiParameter, OpenApiPath, OpenApiSchema,
253};
254
255// Re-export serde for generated code
256pub use serde;
257pub use serde_json;
258
259/// Prelude for convenient imports
260pub mod prelude {
261    // Runtime protocols
262    #[cfg(feature = "cli")]
263    pub use super::CliSubcommand;
264    #[cfg(feature = "http")]
265    pub use super::HttpMount;
266    #[cfg(feature = "jsonrpc")]
267    pub use super::JsonRpcMount;
268    #[cfg(feature = "mcp")]
269    pub use super::McpNamespace;
270    #[cfg(feature = "ws")]
271    pub use super::WsMount;
272    #[cfg(feature = "cli")]
273    pub use super::cli;
274    #[cfg(feature = "graphql")]
275    pub use super::graphql;
276    #[cfg(feature = "graphql")]
277    pub use super::graphql_enum;
278    #[cfg(feature = "graphql")]
279    pub use super::graphql_input;
280    #[cfg(feature = "http")]
281    pub use super::http;
282    #[cfg(feature = "jsonrpc")]
283    pub use super::jsonrpc;
284    #[cfg(feature = "mcp")]
285    pub use super::mcp;
286    #[cfg(any(feature = "http", feature = "openapi"))]
287    pub use super::openapi;
288    #[cfg(feature = "http")]
289    pub use super::response;
290    #[cfg(feature = "http")]
291    pub use super::route;
292    #[cfg(feature = "http")]
293    pub use super::serve;
294    #[cfg(feature = "ws")]
295    pub use super::ws;
296
297    // Schema generators
298    #[cfg(feature = "capnp")]
299    pub use super::capnp;
300    #[cfg(feature = "connect")]
301    pub use super::connect;
302    #[cfg(feature = "grpc")]
303    pub use super::grpc;
304    #[cfg(feature = "smithy")]
305    pub use super::smithy;
306    #[cfg(feature = "thrift")]
307    pub use super::thrift;
308
309    // Specification generators
310    #[cfg(feature = "asyncapi")]
311    pub use super::asyncapi;
312    #[cfg(feature = "jsonschema")]
313    pub use super::jsonschema;
314    #[cfg(feature = "openrpc")]
315    pub use super::openrpc;
316
317    // Documentation generators
318    #[cfg(feature = "markdown")]
319    pub use super::markdown;
320
321    // Blessed presets
322    #[cfg(feature = "cli")]
323    pub use super::program;
324    #[cfg(feature = "jsonrpc")]
325    pub use super::rpc;
326    #[cfg(feature = "http")]
327    pub use super::server;
328    #[cfg(feature = "mcp")]
329    pub use super::tool;
330
331    // Always available
332    pub use super::{Context, ErrorCode, ErrorResponse, IntoErrorCode, ServerlessError};
333
334    // OpenAPI composition (available when any protocol that generates OpenAPI is enabled)
335    #[cfg(feature = "server-less-openapi")]
336    pub use super::OpenApiBuilder;
337    pub use serde::{Deserialize, Serialize};
338
339    // WebSocket sender (when ws feature enabled)
340    #[cfg(feature = "ws")]
341    pub use super::WsSender;
342}