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