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 deps for generated code — users shouldn't need to add these directly
237#[cfg(feature = "clap")]
238#[doc(hidden)]
239pub use clap;
240
241#[cfg(any(feature = "cli", feature = "http"))]
242#[doc(hidden)]
243pub use tokio;
244
245#[cfg(feature = "axum")]
246#[doc(hidden)]
247pub use axum;
248
249// Re-export futures for generated WebSocket code
250#[cfg(feature = "ws")]
251pub use futures;
252
253// Re-export async-graphql for generated GraphQL code
254#[cfg(feature = "graphql")]
255pub use async_graphql;
256#[cfg(feature = "graphql")]
257pub use async_graphql_axum;
258
259// Re-export core types
260pub use server_less_core::*;
261
262// Re-export OpenAPI composition utilities (available when any protocol that generates OpenAPI is enabled)
263#[cfg(feature = "server-less-openapi")]
264pub use server_less_openapi::{
265    OpenApiBuilder, OpenApiError, OpenApiOperation, OpenApiParameter, OpenApiPath, OpenApiSchema,
266};
267
268// Re-export serde for generated code
269pub use serde;
270pub use serde_json;
271
272/// Prelude for convenient imports
273pub mod prelude {
274    // Runtime protocols
275    #[cfg(feature = "cli")]
276    pub use super::CliSubcommand;
277    #[cfg(feature = "http")]
278    pub use super::HttpMount;
279    #[cfg(feature = "jsonrpc")]
280    pub use super::JsonRpcMount;
281    #[cfg(feature = "mcp")]
282    pub use super::McpNamespace;
283    #[cfg(feature = "ws")]
284    pub use super::WsMount;
285    #[cfg(feature = "cli")]
286    pub use super::cli;
287    #[cfg(feature = "graphql")]
288    pub use super::graphql;
289    #[cfg(feature = "graphql")]
290    pub use super::graphql_enum;
291    #[cfg(feature = "graphql")]
292    pub use super::graphql_input;
293    #[cfg(feature = "http")]
294    pub use super::http;
295    #[cfg(feature = "jsonrpc")]
296    pub use super::jsonrpc;
297    #[cfg(feature = "mcp")]
298    pub use super::mcp;
299    #[cfg(any(feature = "http", feature = "openapi"))]
300    pub use super::openapi;
301    #[cfg(feature = "http")]
302    pub use super::response;
303    #[cfg(feature = "http")]
304    pub use super::route;
305    #[cfg(feature = "http")]
306    pub use super::serve;
307    #[cfg(feature = "ws")]
308    pub use super::ws;
309
310    // Schema generators
311    #[cfg(feature = "capnp")]
312    pub use super::capnp;
313    #[cfg(feature = "connect")]
314    pub use super::connect;
315    #[cfg(feature = "grpc")]
316    pub use super::grpc;
317    #[cfg(feature = "smithy")]
318    pub use super::smithy;
319    #[cfg(feature = "thrift")]
320    pub use super::thrift;
321
322    // Specification generators
323    #[cfg(feature = "asyncapi")]
324    pub use super::asyncapi;
325    #[cfg(feature = "jsonschema")]
326    pub use super::jsonschema;
327    #[cfg(feature = "openrpc")]
328    pub use super::openrpc;
329
330    // Documentation generators
331    #[cfg(feature = "markdown")]
332    pub use super::markdown;
333
334    // Blessed presets
335    #[cfg(feature = "cli")]
336    pub use super::program;
337    #[cfg(feature = "jsonrpc")]
338    pub use super::rpc;
339    #[cfg(feature = "http")]
340    pub use super::server;
341    #[cfg(feature = "mcp")]
342    pub use super::tool;
343
344    // Always available
345    pub use super::{Context, ErrorCode, ErrorResponse, IntoErrorCode, ServerlessError};
346
347    // OpenAPI composition (available when any protocol that generates OpenAPI is enabled)
348    #[cfg(feature = "server-less-openapi")]
349    pub use super::OpenApiBuilder;
350    pub use serde::{Deserialize, Serialize};
351
352    // WebSocket sender (when ws feature enabled)
353    #[cfg(feature = "ws")]
354    pub use super::WsSender;
355}