Skip to main content

zlink_macros/
lib.rs

1#![doc(
2    html_logo_url = "https://raw.githubusercontent.com/z-galaxy/zlink/3660d731d7de8f60c8d82e122b3ece15617185e4/data/logo.png"
3)]
4#![deny(
5    missing_debug_implementations,
6    nonstandard_style,
7    rust_2018_idioms,
8    missing_docs
9)]
10#![warn(unreachable_pub)]
11#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
12
13mod utils;
14
15#[cfg(feature = "introspection")]
16mod introspect;
17
18mod reply_error;
19
20#[cfg(feature = "proxy")]
21mod proxy;
22
23#[cfg(feature = "service")]
24mod service;
25
26/// Derives `Type` for structs and enums, generating appropriate `Type::Object` or `Type::Enum`
27/// representation.
28///
29/// **Requires the `introspection` feature to be enabled.**
30///
31/// ## Structs
32///
33/// For structs, this macro supports named fields and unit structs. It will generate a
34/// `Type` implementation that creates a `Type::Object` containing all the fields with their
35/// names and types. Tuple structs are not supported as Varlink does not support unnamed fields.
36///
37/// ## Enums
38///
39/// For enums, this macro only supports unit variants (variants without associated data). It will
40/// generate a `Type` implementation that creates a `Type::Enum` containing all the variant
41/// names.
42///
43/// # Supported Attributes
44///
45/// The following attributes can be used to customize the behavior of this derive macro:
46///
47/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
48///   `::zlink`.
49///
50/// # Limitations
51///
52/// The following types are **not** supported by this macro:
53///
54/// - **Tuple structs**: Varlink does not support unnamed fields
55/// - **Enums with data**: Only unit enums (variants without associated data) are supported
56/// - **Unions**: Not supported by Varlink
57///
58/// ```rust,compile_fail
59/// # use zlink::introspect::Type;
60/// #[derive(Type)]  // This will fail to compile
61/// struct Point(f32, f32, f32);
62/// ```
63///
64/// ```rust,compile_fail
65/// # use zlink::introspect::Type;
66/// #[derive(Type)]  // This will fail to compile
67/// enum Status {
68///     Active(String),  // Variants with data are not supported
69///     Inactive,
70/// }
71/// ```
72///
73/// # Examples
74///
75/// ## Named Structs
76///
77/// ```rust
78/// use zlink::introspect::Type;
79/// use zlink::idl;
80///
81/// #[derive(Type)]
82/// struct Person {
83///     name: String,
84///     age: i32,
85///     active: bool,
86/// }
87///
88/// // Access the generated type information
89/// match Person::TYPE {
90///     idl::Type::Object(fields) => {
91///         let field_vec: Vec<_> = fields.iter().collect();
92///         assert_eq!(field_vec.len(), 3);
93///
94///         assert_eq!(field_vec[0].name(), "name");
95///         assert_eq!(field_vec[0].ty(), &idl::Type::String);
96///
97///         assert_eq!(field_vec[1].name(), "age");
98///         assert_eq!(field_vec[1].ty(), &idl::Type::Int);
99///
100///         assert_eq!(field_vec[2].name(), "active");
101///         assert_eq!(field_vec[2].ty(), &idl::Type::Bool);
102///     }
103///     _ => panic!("Expected struct type"),
104/// }
105/// ```
106///
107/// ## Unit Structs
108///
109/// ```rust
110/// # use zlink::introspect::Type;
111/// # use zlink::idl;
112/// #[derive(Type)]
113/// struct Unit;
114///
115/// // Unit structs generate empty field lists
116/// match Unit::TYPE {
117///     idl::Type::Object(fields) => {
118///         assert_eq!(fields.len(), 0);
119///     }
120///     _ => panic!("Expected struct type"),
121/// }
122/// ```
123///
124/// ## Complex Types
125///
126/// ```rust
127/// # use zlink::introspect::Type;
128/// # use zlink::idl;
129/// #[derive(Type)]
130/// struct Complex {
131///     id: u64,
132///     description: Option<String>,
133///     tags: Vec<String>,
134/// }
135///
136/// // The macro handles nested types like Option<T> and Vec<T>
137/// match Complex::TYPE {
138///     idl::Type::Object(fields) => {
139///         let field_vec: Vec<_> = fields.iter().collect();
140///
141///         // Optional field becomes Type::Optional
142///         match field_vec[1].ty() {
143///             idl::Type::Optional(inner) => assert_eq!(inner.inner(), &idl::Type::String),
144///             _ => panic!("Expected optional type"),
145///         }
146///
147///         // Vec field becomes Type::Array
148///         match field_vec[2].ty() {
149///             idl::Type::Array(inner) => assert_eq!(inner.inner(), &idl::Type::String),
150///             _ => panic!("Expected array type"),
151///         }
152///     }
153///     _ => panic!("Expected struct type"),
154/// }
155/// ```
156///
157/// ## Unit Enums
158///
159/// ```rust
160/// # use zlink::introspect::Type;
161/// # use zlink::idl;
162/// #[derive(Type)]
163/// enum Status {
164///     Active,
165///     Inactive,
166///     Pending,
167/// }
168///
169/// // Unit enums generate variant lists
170/// match Status::TYPE {
171///     idl::Type::Enum(variants) => {
172///         let variant_vec: Vec<_> = variants.iter().collect();
173///         assert_eq!(variant_vec.len(), 3);
174///         assert_eq!(variant_vec[0].name(), "Active");
175///         assert_eq!(variant_vec[1].name(), "Inactive");
176///         assert_eq!(variant_vec[2].name(), "Pending");
177///     }
178///     _ => panic!("Expected enum type"),
179/// }
180/// ```
181#[cfg(feature = "introspection")]
182#[proc_macro_derive(IntrospectType, attributes(zlink))]
183pub fn derive_introspect_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
184    introspect::r#type::derive_type(input)
185}
186
187/// Derives `Type` for structs and enums, generating named custom type definitions.
188///
189/// **Requires the `introspection` feature to be enabled.**
190///
191/// This macro generates implementations of the `CustomType` trait, which provides named
192/// custom type definitions suitable for IDL generation. It also generates a `Type` implementation
193/// and therefore is mutually exclusive to `zlink::introspect::Type` derive macro.
194///
195/// ## Structs
196///
197/// For structs, this macro generates a `custom::Type::Object` containing the struct name and
198/// all fields with their names and types.
199///
200/// ## Enums
201///
202/// For enums, this macro only supports unit variants and generates a `custom::Type::Enum`
203/// containing the enum name and all variant names.
204///
205/// # Supported Attributes
206///
207/// The following attributes can be used to customize the behavior of this derive macro:
208///
209/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
210///   `::zlink`.
211///
212/// # Examples
213///
214/// ## Named Structs
215///
216/// ```rust
217/// use zlink::introspect::{CustomType, Type};
218/// use zlink::idl;
219///
220/// #[derive(CustomType)]
221/// struct Point {
222///     x: f64,
223///     y: f64,
224/// }
225///
226/// // Access the generated custom type information
227/// match Point::CUSTOM_TYPE {
228///     idl::CustomType::Object(obj) => {
229///         assert_eq!(obj.name(), "Point");
230///         let fields: Vec<_> = obj.fields().collect();
231///         assert_eq!(fields.len(), 2);
232///         assert_eq!(fields[0].name(), "x");
233///         assert_eq!(fields[1].name(), "y");
234///     }
235///     _ => panic!("Expected custom object type"),
236/// }
237///
238/// match Point::TYPE {
239///     idl::Type::Custom(name) => {
240///         assert_eq!(*name, "Point");
241///     }
242///     _ => panic!("Expected custom type"),
243/// }
244/// ```
245///
246/// ## Unit Enums
247///
248/// ```rust
249/// # use zlink::introspect::{CustomType, Type};
250/// # use zlink::idl;
251/// #[derive(CustomType)]
252/// enum Status {
253///     Active,
254///     Inactive,
255///     Pending,
256/// }
257///
258/// // Access the generated custom enum type information
259/// match Status::CUSTOM_TYPE {
260///     idl::CustomType::Enum(enm) => {
261///         assert_eq!(enm.name(), "Status");
262///         let variants: Vec<_> = enm.variants().collect();
263///         assert_eq!(variants.len(), 3);
264///         assert_eq!(variants[0].name(), "Active");
265///         assert_eq!(variants[1].name(), "Inactive");
266///         assert_eq!(variants[2].name(), "Pending");
267///     }
268///     _ => panic!("Expected custom enum type"),
269/// }
270///
271/// match Status::TYPE {
272///     idl::Type::Custom(name) => {
273///         assert_eq!(*name, "Status");
274///     }
275///     _ => panic!("Expected custom type"),
276/// }
277/// ```
278#[cfg(feature = "introspection")]
279#[proc_macro_derive(IntrospectCustomType, attributes(zlink))]
280pub fn derive_introspect_custom_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
281    introspect::custom_type::derive_custom_type(input)
282}
283
284/// Derives `ReplyError` for enums, generating error definitions for Varlink service errors.
285///
286/// **Requires the `introspection` feature to be enabled.**
287///
288/// This macro generates implementations of the `ReplyError` trait, which provides a list of
289/// error variants that can be returned by a Varlink service method. It supports unit variants,
290/// variants with named fields, and single-field tuple variants (where the field type implements
291/// `Type` and has a `Type::Object`).
292///
293/// # Supported Attributes
294///
295/// The following attributes can be used to customize the behavior of this derive macro:
296///
297/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
298///   `::zlink`.
299///
300/// # Example
301///
302/// ```rust
303/// use zlink::introspect::ReplyError;
304///
305/// #[derive(ReplyError)]
306/// enum ServiceError {
307///     // Unit variant - no parameters
308///     NotFound,
309///
310///     // Named field variant - multiple parameters
311///     InvalidQuery {
312///         message: String,
313///         line: u32,
314///     },
315///
316///     // Single tuple variant - uses fields from the wrapped type
317///     ValidationFailed(ValidationDetails),
318/// }
319///
320/// // Example struct for tuple variant
321/// #[derive(zlink::introspect::Type)]
322/// struct ValidationDetails {
323///     field_name: String,
324///     expected: String,
325/// }
326///
327/// // Access the generated error variants
328/// assert_eq!(ServiceError::VARIANTS.len(), 3);
329/// assert_eq!(ServiceError::VARIANTS[0].name(), "NotFound");
330/// assert!(ServiceError::VARIANTS[0].has_no_fields());
331///
332/// assert_eq!(ServiceError::VARIANTS[1].name(), "InvalidQuery");
333/// assert!(!ServiceError::VARIANTS[1].has_no_fields());
334/// ```
335#[cfg(feature = "introspection")]
336#[proc_macro_derive(IntrospectReplyError, attributes(zlink))]
337pub fn derive_introspect_reply_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
338    introspect::reply_error::derive_reply_error(input)
339}
340
341/// Creates a client-side proxy for calling Varlink methods on a connection.
342///
343/// **Requires the `proxy` feature to be enabled.**
344///
345/// This attribute macro generates an implementation of the provided trait for `Connection<S>`,
346/// automatically handling the serialization of method calls and deserialization of responses.
347/// Each proxy trait targets a single Varlink interface.
348///
349/// The macro also generates a chain extension trait that allows you to chain multiple method
350/// calls together for efficient batching across multiple interfaces.
351///
352/// # Supported Attributes
353///
354/// The following attributes can be used to customize the behavior of this macro:
355///
356/// * `interface` (required) - The Varlink interface name (e.g., `"org.varlink.service"`).
357/// * `crate` - Specifies the crate path to use for zlink types. Defaults to `::zlink`.
358/// * `chain_name` - Custom name for the generated chain extension trait. Defaults to
359///   `{TraitName}Chain`.
360///
361/// # Example
362///
363/// ```rust
364/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
365/// use zlink::proxy;
366/// use serde::{Deserialize, Serialize};
367/// use serde_prefix_all::prefix_all;
368/// use futures_util::stream::Stream;
369///
370/// #[proxy("org.example.MyService")]
371/// trait MyServiceProxy {
372///     // Non-streaming methods can use borrowed types.
373///     async fn get_status(&mut self) -> zlink::Result<Result<Status<'_>, MyError<'_>>>;
374///     async fn set_value(
375///         &mut self,
376///         key: &str,
377///         value: i32,
378///     ) -> zlink::Result<Result<(), MyError<'_>>>;
379///     #[zlink(rename = "ListMachines")]
380///     async fn list_machines(&mut self) -> zlink::Result<Result<Vec<Machine<'_>>, MyError<'_>>>;
381///     // Streaming methods must use owned types (DeserializeOwned) because the internal buffer may
382///     // be reused between stream iterations.
383///     #[zlink(rename = "GetStatus", more)]
384///     async fn stream_status(
385///         &mut self,
386///     ) -> zlink::Result<
387///         impl Stream<Item = zlink::Result<Result<OwnedStatus, OwnedMyError>>>,
388///     >;
389/// }
390///
391/// // The macro generates:
392/// // impl<S: Socket> MyServiceProxy for Connection<S> { ... }
393///
394/// // Borrowed types for non-streaming methods.
395/// #[derive(Debug, Serialize, Deserialize)]
396/// struct Status<'m> {
397///     active: bool,
398///     message: &'m str,
399/// }
400///
401/// #[derive(Debug, Serialize, Deserialize)]
402/// struct Machine<'m> { name: &'m str }
403///
404/// #[prefix_all("org.example.MyService.")]
405/// #[derive(Debug, Serialize, Deserialize)]
406/// #[serde(tag = "error", content = "parameters")]
407/// enum MyError<'a> {
408///     NotFound,
409///     InvalidRequest,
410///     // Parameters must be named.
411///     CodedError { code: u32, message: &'a str },
412/// }
413///
414/// // Owned types for streaming methods (required by the `more` attribute).
415/// #[derive(Debug, Serialize, Deserialize)]
416/// struct OwnedStatus {
417///     active: bool,
418///     message: String,
419/// }
420///
421/// #[prefix_all("org.example.MyService.")]
422/// #[derive(Debug, Serialize, Deserialize)]
423/// #[serde(tag = "error", content = "parameters")]
424/// enum OwnedMyError {
425///     NotFound,
426///     InvalidRequest,
427///     CodedError { code: u32, message: String },
428/// }
429///
430/// // Example usage:
431/// # use zlink::test_utils::mock_socket::MockSocket;
432/// # let responses = vec![
433/// #     r#"{"parameters":{"active":true,"message":"System running"}}"#,
434/// # ];
435/// # let socket = MockSocket::with_responses(&responses);
436/// # let mut conn = zlink::Connection::new(socket);
437/// let result = conn.get_status().await?.unwrap();
438/// assert_eq!(result.active, true);
439/// assert_eq!(result.message, "System running");
440/// # Ok::<(), Box<dyn std::error::Error>>(())
441/// # }).unwrap();
442/// ```
443///
444/// # Chaining Method Calls
445///
446/// The proxy macro generates chain extension traits that allow you to batch multiple method calls
447/// together. This is useful for reducing round trips and efficiently calling methods across
448/// multiple interfaces. Each method gets a `chain_` prefixed variant that starts a chain.
449///
450/// **Important**: Chain methods are only generated for proxy methods that use owned types
451/// (`DeserializeOwned`) in their return type. Methods with borrowed types (non-static lifetimes)
452/// don't get chain variants since the internal buffer may be reused between stream iterations.
453/// Input arguments can still use borrowed types.
454///
455/// ## Example: Chaining Method Calls
456///
457/// ```rust
458/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
459/// # use zlink::proxy;
460/// # use serde::{Deserialize, Serialize};
461/// # use futures_util::{pin_mut, TryStreamExt};
462/// #
463/// // Owned reply types for chain API.
464/// # #[derive(Debug, Serialize, Deserialize)]
465/// # struct User { id: u64, name: String }
466/// # #[derive(Debug, Serialize, Deserialize)]
467/// # struct Post { id: u64, user_id: u64, content: String }
468/// # #[derive(Debug, Serialize, Deserialize)]
469/// # #[serde(untagged)]
470/// # enum BlogReply {
471/// #     User(User),
472/// #     Post(Post),
473/// #     Posts(Vec<Post>)
474/// # }
475/// # #[derive(Debug, Serialize, Deserialize)]
476/// # #[serde(tag = "error")]
477/// # enum BlogError { NotFound, InvalidInput }
478/// # impl std::fmt::Display for BlogError {
479/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
480/// #         match self {
481/// #             Self::NotFound => write!(f, "Not found"),
482/// #             Self::InvalidInput => write!(f, "Invalid input")
483/// #         }
484/// #     }
485/// # }
486/// # impl std::error::Error for BlogError {}
487/// #
488/// // Define proxies with owned return types - chain methods are generated.
489/// #[proxy("org.example.blog.Users")]
490/// trait UsersProxy {
491///     async fn get_user(&mut self, id: u64)
492///         -> zlink::Result<Result<BlogReply, BlogError>>;
493///     async fn create_user(&mut self, name: &str)
494///         -> zlink::Result<Result<BlogReply, BlogError>>;
495/// }
496///
497/// #[proxy("org.example.blog.Posts")]
498/// trait PostsProxy {
499///     async fn get_posts_by_user(&mut self, user_id: u64)
500///         -> zlink::Result<Result<BlogReply, BlogError>>;
501///     async fn create_post(&mut self, user_id: u64, content: &str)
502///         -> zlink::Result<Result<BlogReply, BlogError>>;
503/// }
504///
505/// # use zlink::test_utils::mock_socket::MockSocket;
506/// # let responses = vec![
507/// #     r#"{"parameters":{"id":1,"name":"Alice"}}"#,
508/// #     r#"{"parameters":{"id":1,"user_id":1,"content":"My first post!"}}"#,
509/// #     r#"{"parameters":[{"id":1,"user_id":1,"content":"My first post!"}]}"#,
510/// #     r#"{"parameters":{"id":1,"name":"Alice"}}"#,
511/// # ];
512/// # let socket = MockSocket::with_responses(&responses);
513/// # let mut conn = zlink::Connection::new(socket);
514/// let chain = conn
515///     .chain_create_user("Alice")?
516///     .create_post(1, "My first post!")?
517///     .get_posts_by_user(1)?
518///     .get_user(1)?;
519///
520/// // Send all calls in a single batch.
521/// let replies = chain.send::<BlogReply, BlogError>().await?;
522/// pin_mut!(replies);
523///
524/// // Process replies in order.
525/// let mut reply_count = 0;
526/// while let Some((reply, _fds)) = replies.try_next().await? {
527///     reply_count += 1;
528///     if let Ok(response) = reply {
529///         match response.parameters() {
530///             Some(BlogReply::User(user)) => assert_eq!(user.name, "Alice"),
531///             Some(BlogReply::Post(post)) => assert_eq!(post.content, "My first post!"),
532///             Some(BlogReply::Posts(posts)) => assert_eq!(posts.len(), 1),
533///             None => {} // set_value returns empty response
534///         }
535///     }
536/// }
537/// assert_eq!(reply_count, 4); // We made 4 calls
538/// # Ok::<(), Box<dyn std::error::Error>>(())
539/// # }).unwrap();
540/// ```
541///
542/// ## Combining Multiple Services
543///
544/// You can chain calls across multiple custom services. Define a combined reply type that can
545/// deserialize responses from all interfaces:
546///
547/// ```rust
548/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
549/// # use zlink::proxy;
550/// # use serde::{Deserialize, Serialize};
551/// # use futures_util::{pin_mut, TryStreamExt};
552/// #
553/// # #[derive(Debug, Serialize, Deserialize)]
554/// # struct Status { active: bool, message: String }
555/// # #[derive(Debug, Serialize, Deserialize)]
556/// # struct HealthInfo { healthy: bool, uptime: u64 }
557/// # #[derive(Debug, Serialize, Deserialize)]
558/// # #[serde(tag = "error")]
559/// # enum ServiceError { NotFound, InvalidRequest }
560/// # impl std::fmt::Display for ServiceError {
561/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
562/// #         match self {
563/// #             Self::NotFound => write!(f, "Not found"),
564/// #             Self::InvalidRequest => write!(f, "Invalid input")
565/// #         }
566/// #     }
567/// # }
568/// # impl std::error::Error for ServiceError {}
569/// #
570/// // Multiple proxies with owned return types.
571/// #[proxy("com.example.StatusService")]
572/// trait StatusProxy {
573///     async fn get_status(&mut self) -> zlink::Result<Result<Status, ServiceError>>;
574/// }
575///
576/// #[proxy("com.example.HealthService")]
577/// trait HealthProxy {
578///     async fn get_health(&mut self) -> zlink::Result<Result<HealthInfo, ServiceError>>;
579/// }
580///
581/// // Combined reply type for cross-interface chaining.
582/// #[derive(Debug, Deserialize)]
583/// #[serde(untagged)]
584/// enum CombinedReply {
585///     Status(Status),
586///     Health(HealthInfo),
587/// }
588///
589/// # use zlink::test_utils::mock_socket::MockSocket;
590/// # let responses = vec![
591/// #     r#"{"parameters":{"active":true,"message":"Running"}}"#,
592/// #     r#"{"parameters":{"healthy":true,"uptime":12345}}"#,
593/// # ];
594/// # let socket = MockSocket::with_responses(&responses);
595/// # let mut conn = zlink::Connection::new(socket);
596/// // Chain calls across both services.
597/// let chain = conn
598///     .chain_get_status()?
599///     .get_health()?;
600///
601/// let replies = chain.send::<CombinedReply, ServiceError>().await?;
602/// pin_mut!(replies);
603///
604/// let mut count = 0;
605/// while let Some((reply, _fds)) = replies.try_next().await? {
606///     count += 1;
607///     if let Ok(response) = reply {
608///         match response.parameters() {
609///             Some(CombinedReply::Status(s)) => println!("Status: {}", s.message),
610///             Some(CombinedReply::Health(h)) => println!("Uptime: {}", h.uptime),
611///             None => {}
612///         }
613///     }
614/// }
615/// assert_eq!(count, 2);
616/// # Ok::<(), Box<dyn std::error::Error>>(())
617/// # }).unwrap();
618/// ```
619///
620/// ## Chain Extension Traits
621///
622/// For each proxy trait, the macro generates a corresponding chain extension trait. For example,
623/// `FtlProxy` gets `FtlProxyChain`. This trait is automatically implemented for `Chain` types,
624/// allowing seamless method chaining across interfaces.
625///
626/// # Method Requirements
627///
628/// Proxy methods must:
629/// - Take `&mut self` as the first parameter
630/// - Can be either `async fn` or return `impl Future`
631/// - Return `zlink::Result<Result<ReplyType, ErrorType>>` (outer Result for connection errors,
632///   inner for method errors)
633/// - The arguments can be any type that implement `serde::Serialize`
634/// - The reply type (`Ok` case of the inner `Result`) must be a type that implements
635///   `serde::Deserialize` and deserializes itself from a JSON object. Typically you'd just use a
636///   struct that derives `serde::Deserialize`.
637/// - The reply error type (`Err` case of the inner `Result`) must be a type `serde::Deserialize`
638///   that deserializes itself from a JSON object with two fields:
639///   - `error`: a string containing the fully qualified error name
640///   - `parameters`: an optional object containing all the fields of the error
641///
642/// # Method Names
643///
644/// By default, method names are converted from snake_case to PascalCase for the Varlink call.
645/// To specify a different Varlink method name, use the `#[zlink(rename = "...")]` attribute. See
646/// `list_machines` in the example above.
647///
648/// # Streaming Methods
649///
650/// For methods that support streaming (the 'more' flag), use the `#[zlink(more)]` attribute.
651/// Streaming methods must return `Result<impl Stream<Item = Result<Result<ReplyType,
652/// ErrorType>>>>`. The proxy will automatically set the 'more' flag on the call and return a
653/// stream of replies.
654///
655/// # One-way Methods
656///
657/// For fire-and-forget methods that don't expect a reply, use the `#[zlink(oneway)]` attribute.
658/// One-way methods send the call and return immediately without waiting for a response. The method
659/// must return `zlink::Result<()>` (just the outer Result for connection errors, no inner Result
660/// since there's no reply to process).
661///
662/// One-way methods cannot be combined with `#[zlink(more)]` or `#[zlink(return_fds)]`.
663///
664/// This attribute is particularly useful in combination with chaining method calls. When you chain
665/// oneway methods with regular methods, the oneway calls are sent but don't contribute to the reply
666/// stream. For example, if you chain 4 calls where 2 are regular and 2 are oneway, you'll only
667/// receive 2 replies. This allows you to efficiently batch side-effect operations (like resets or
668/// notifications) alongside queries in a single round-trip.
669///
670/// ```rust
671/// # use zlink::proxy;
672/// # use serde::{Deserialize, Serialize};
673/// #[proxy("org.example.Notifications")]
674/// trait NotificationsProxy {
675///     /// Fire-and-forget notification - returns immediately without waiting for a reply.
676///     #[zlink(oneway)]
677///     async fn notify(&mut self, message: &str) -> zlink::Result<()>;
678///
679///     /// Another one-way method with multiple parameters.
680///     #[zlink(oneway)]
681///     async fn log_event(&mut self, level: &str, message: &str, timestamp: u64)
682///         -> zlink::Result<()>;
683/// }
684/// ```
685///
686/// # File Descriptor Passing
687///
688/// **Requires the `std` feature to be enabled.**
689///
690/// Methods can send and receive file descriptors using the following attributes:
691///
692/// ## Sending File Descriptors
693///
694/// Use `#[zlink(fds)]` on a parameter of type `Vec<OwnedFd>` to send file descriptors with the
695/// method call. Only one FD parameter is allowed per method.
696///
697/// ## Receiving File Descriptors
698///
699/// Use `#[zlink(return_fds)]` on a method to indicate it returns file descriptors. The method's
700/// return type must be `Result<(Result<ReplyType, ErrorType>, Vec<OwnedFd>)>` - a tuple containing
701/// both the method result and the received file descriptors. The FDs are available regardless of
702/// whether the method succeeded or failed.
703///
704/// ## Example: File Descriptor Passing
705///
706/// File descriptors are passed out-of-band from the encoded JSON parameters. The typical pattern
707/// is to include integer indexes in your JSON parameters that reference positions in the FD
708/// vector. This is similar to how D-Bus handles FD passing.
709///
710/// ```rust
711/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
712/// use zlink::proxy;
713/// use serde::{Deserialize, Serialize};
714/// use std::os::fd::OwnedFd;
715///
716/// #[proxy("org.example.FileService")]
717/// trait FileServiceProxy {
718///     // Send file descriptors to the service
719///     // The stdin/stdout parameters are indexes into the FDs vector
720///     async fn spawn_process(
721///         &mut self,
722///         command: String,
723///         stdin_fd: u32,
724///         stdout_fd: u32,
725///         #[zlink(fds)] fds: Vec<OwnedFd>,
726///     ) -> zlink::Result<Result<ProcessInfo, FileError>>;
727///
728///     // Receive file descriptors from the service
729///     // Returns metadata with FD indexes and the actual FDs
730///     #[zlink(return_fds)]
731///     async fn open_files(
732///         &mut self,
733///         paths: Vec<String>,
734///     ) -> zlink::Result<(Result<Vec<FileInfo>, FileError>, Vec<OwnedFd>)>;
735/// }
736///
737/// #[derive(Debug, Serialize, Deserialize)]
738/// struct ProcessInfo {
739///     pid: u32,
740/// }
741///
742/// // Response contains FD indexes referencing the FD vector
743/// #[derive(Debug, Serialize, Deserialize)]
744/// struct FileInfo {
745///     path: String,
746///     fd: u32,  // Index into the FD vector (0, 1, 2, etc.)
747/// }
748///
749/// #[derive(Debug, Serialize, Deserialize)]
750/// #[serde(tag = "error")]
751/// enum FileError {
752///     NotFound { path: String },
753///     PermissionDenied { path: String },
754/// }
755/// # impl std::error::Error for FileError {}
756/// # impl std::fmt::Display for FileError {
757/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
758/// #         match self {
759/// #             FileError::NotFound { path } => write!(f, "File not found: {}", path),
760/// #             FileError::PermissionDenied { path } =>
761/// #                 write!(f, "Permission denied: {}", path),
762/// #         }
763/// #     }
764/// # }
765///
766/// // Example usage:
767/// # use std::os::unix::net::UnixStream;
768/// # use zlink::test_utils::mock_socket::MockSocket;
769/// // Sending FDs: Pass indexes as regular parameters
770/// # let (stdin_pipe, _w1) = UnixStream::pair()?;
771/// # let (stdout_pipe, _w2) = UnixStream::pair()?;
772/// # let send_response = r#"{"parameters":{"pid":1234}}"#;
773/// # let send_socket = MockSocket::with_responses(&[send_response]);
774/// # let mut send_conn = zlink::Connection::new(send_socket);
775/// let fds = vec![stdin_pipe.into(), stdout_pipe.into()];
776/// // Parameters reference FD indexes: stdin_fd=0, stdout_fd=1
777/// let result = send_conn.spawn_process("/bin/cat".to_string(), 0, 1, fds).await?;
778/// let process_info = result?;
779/// assert_eq!(process_info.pid, 1234);
780///
781/// // Receiving FDs: Response contains indexes that reference the FD vector
782/// # let (file1, _w3) = UnixStream::pair()?;
783/// # let (file2, _w4) = UnixStream::pair()?;
784/// # let recv_response = r#"{
785/// #   "parameters": [
786/// #     {"path": "/etc/config.txt", "fd": 0},
787/// #     {"path": "/var/data.bin", "fd": 1}
788/// #   ]
789/// # }"#;
790/// # let recv_fds = vec![vec![file1.into(), file2.into()]];
791/// # let recv_socket = MockSocket::new(&[recv_response], recv_fds);
792/// # let mut recv_conn = zlink::Connection::new(recv_socket);
793/// let (result, received_fds) = recv_conn
794///     .open_files(vec!["/etc/config.txt".to_string(), "/var/data.bin".to_string()])
795///     .await?;
796/// let file_list = result?;
797/// assert_eq!(file_list.len(), 2);
798/// assert_eq!(received_fds.len(), 2);
799/// // Use the fd field to match file info with actual FDs
800/// for file_info in &file_list {
801///     let fd = &received_fds[file_info.fd as usize];
802///     println!("File {} has FD at index {}", file_info.path, file_info.fd);
803/// }
804/// # Ok::<(), Box<dyn std::error::Error>>(())
805/// # }).unwrap();
806/// ```
807///
808/// ## Parameter Renaming
809///
810/// Use `#[zlink(rename = "name")]` on parameters to customize their serialized names in the
811/// Varlink protocol. This is useful when the Rust parameter name doesn't match the expected
812/// Varlink parameter name.
813///
814/// ```rust
815/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
816/// # use zlink::proxy;
817/// # use serde::{Deserialize, Serialize};
818/// #[proxy("org.example.Users")]
819/// trait UsersProxy {
820///     async fn create_user(
821///         &mut self,
822///         #[zlink(rename = "user_name")] name: String,
823///         #[zlink(rename = "user_email")] email: String,
824///     ) -> zlink::Result<Result<UserId, UserError>>;
825/// }
826/// # #[derive(Debug, Serialize, Deserialize)]
827/// # struct UserId { id: u32 }
828/// # #[derive(Debug, Serialize, Deserialize)]
829/// # #[serde(tag = "error")]
830/// # enum UserError { InvalidInput }
831/// # Ok::<(), Box<dyn std::error::Error>>(())
832/// # }).unwrap();
833/// ```
834///
835/// # Generic Parameters
836///
837/// The proxy macro supports generic type parameters on individual methods. Note that generic
838/// parameters on the trait itself are not currently supported.
839///
840/// ```rust
841/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
842/// # use zlink::proxy;
843/// # use serde::{Deserialize, Serialize};
844/// # #[derive(Debug, Serialize, Deserialize)]
845/// # struct StoredValue<T> { data: T }
846/// # #[derive(Debug, Serialize, Deserialize)]
847/// # struct ProcessReply<'a> { result: &'a str }
848/// # #[derive(Debug, Serialize, Deserialize)]
849/// # #[serde(tag = "error")]
850/// # enum StorageError { NotFound }
851/// # impl std::fmt::Display for StorageError {
852/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
853/// #         write!(f, "Storage error")
854/// #     }
855/// # }
856/// # impl std::error::Error for StorageError {}
857/// #
858/// #[proxy("org.example.Storage")]
859/// trait StorageProxy {
860///     // Method-level generics with trait bounds
861///     async fn store<'a, T: Serialize + std::fmt::Debug>(
862///         &mut self,
863///         key: &'a str,
864///         value: T,
865///     ) -> zlink::Result<Result<(), StorageError>>;
866///
867///     // Generic methods with where clauses
868///     async fn process<T>(&mut self, data: T)
869///         -> zlink::Result<Result<ProcessReply<'_>, StorageError>>
870///     where
871///         T: Serialize + std::fmt::Debug;
872///
873///     // Methods can use generic type parameters in both input and output
874///     async fn store_and_return<'a, T>(&mut self, key: &'a str, value: T)
875///         -> zlink::Result<Result<StoredValue<T>, StorageError>>
876///     where
877///         T: Serialize + for<'de> Deserialize<'de> + std::fmt::Debug;
878/// }
879///
880/// // Example usage:
881/// # use zlink::test_utils::mock_socket::MockSocket;
882/// # let responses = vec![
883/// #     r#"{"parameters":null}"#, // store returns empty Ok
884/// # ];
885/// # let socket = MockSocket::with_responses(&responses);
886/// # let mut conn = zlink::Connection::new(socket);
887/// // Store a value with generic type
888/// let result = conn.store("my-key", 42i32).await?;
889/// assert!(result.is_ok());
890/// # Ok::<(), Box<dyn std::error::Error>>(())
891/// # }).unwrap();
892/// ```
893#[cfg(feature = "proxy")]
894#[proc_macro_attribute]
895pub fn proxy(
896    attr: proc_macro::TokenStream,
897    input: proc_macro::TokenStream,
898) -> proc_macro::TokenStream {
899    proxy::proxy(attr.into(), input.into()).into()
900}
901
902/// Implements `serde::{Serialize, Deserialize}` for service error enums.
903///
904/// This macro automatically generates both `Serialize` and `Deserialize` implementations for error
905/// types that are used in Varlink service replies.
906///
907/// The macro works in both `std` and `no_std` environments and requires the "error" field
908/// to appear before "parameters" field in JSON for efficient parsing.
909///
910/// # Supported Enum Variants
911///
912/// The macro supports:
913/// - **Unit variants**: Variants without any data
914/// - **Named field variants**: Variants with named fields
915///
916/// Tuple variants are **not** supported.
917///
918/// # Attributes
919///
920/// ## Enum-level attributes
921///
922/// - `interface` - This mandatory attribute specifies the Varlink interface name (e.g.,
923///   "org.varlink.service")
924///
925/// ## Field-level attributes
926///
927/// - `rename = "..."` - Specifies a custom name for the field in the JSON representation
928/// - `borrow` - Enables zero-copy deserialization for types like `Cow<'_, str>`
929///
930/// # Example
931///
932/// ```rust
933/// use std::borrow::Cow;
934/// use zlink::ReplyError;
935///
936/// #[derive(ReplyError)]
937/// #[zlink(interface = "com.example.MyService")]
938/// enum ServiceError<'a> {
939///     // Unit variant - no parameters
940///     NotFound,
941///     PermissionDenied,
942///
943///     // Named field variant - multiple parameters
944///     InvalidInput {
945///         field: String,
946///         reason: String,
947///     },
948///
949///     // Variant with zero-copy deserialization using borrow
950///     CustomError {
951///         #[zlink(borrow)]
952///         message: Cow<'a, str>,
953///     },
954///
955///     // Variant with renamed field
956///     Timeout {
957///         #[zlink(rename = "timeoutSeconds")]
958///         seconds: u32,
959///     },
960/// }
961///
962/// // The macro generates:
963/// // - `Serialize` impl that creates properly tagged enum format
964/// // - `Deserialize` impl that handles the tagged enum format efficiently
965/// ```
966///
967/// # Serialization Format
968///
969/// The generated serialization uses a tagged enum format:
970///
971/// ```json
972/// // Unit variant:
973/// {"error": "NotFound"}
974/// // or with empty parameters:
975/// {"error": "NotFound", "parameters": null}
976///
977/// // Variant with fields:
978/// {
979///   "error": "InvalidInput",
980///   "parameters": {
981///     "field": "username",
982///     "reason": "too short"
983///   }
984/// }
985/// ```
986#[proc_macro_derive(ReplyError, attributes(zlink))]
987pub fn derive_reply_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
988    reply_error::derive_reply_error(input)
989}
990
991/// Transforms an impl block into a `Service` trait implementation.
992///
993/// **Requires the `service` feature to be enabled.** The `service` feature automatically enables
994/// the `introspection` feature, allowing the macro to generate interface descriptions and handle
995/// the standard `org.varlink.service` interface automatically.
996///
997/// This attribute macro takes a regular impl block and generates the necessary code to implement
998/// the `Service` trait, enabling the type to handle Varlink method calls.
999///
1000/// # Automatic Introspection Support
1001///
1002/// The generated service automatically handles the `org.varlink.service` interface:
1003///
1004/// - **`GetInfo`**: Returns service metadata (vendor, product, version, URL) and a list of all
1005///   implemented interfaces.
1006/// - **`GetInterfaceDescription`**: Returns the IDL description for any implemented interface,
1007///   generated at compile time from the method signatures and types.
1008/// - **Unknown methods**: Return `MethodNotFound` error with the method name.
1009/// - **Unknown interfaces** (in `GetInterfaceDescription`): Return `InterfaceNotFound` error.
1010///
1011/// # Supported Attributes
1012///
1013/// ## On the impl block:
1014///
1015/// * `crate = "path"` - Specifies the crate path to use for zlink types. Defaults to `::zlink`.
1016/// * `interface = "..."` - Sets the default interface name for all methods. Useful for services
1017///   that implement a single interface. Methods can still override this with method-level
1018///   `#[zlink(interface = "...")]`.
1019/// * `types = [Type1, Type2, ...]` - Custom types to include in interface descriptions. These types
1020///   must implement `CustomType` (typically via `#[derive(CustomType)]`). For single-interface
1021///   services, these types are included in that interface's IDL output. For multi-interface
1022///   services, prefer specifying types per interface using the method-level `#[zlink(interface =
1023///   "...", types = [...])]` attribute instead.
1024/// * `vendor = <expr>` - The vendor name for `GetInfo` response. Defaults to empty string.
1025/// * `product = <expr>` - The product name for `GetInfo` response. Defaults to empty string.
1026/// * `version = <expr>` - The version string for `GetInfo` response. Defaults to empty string. E.g.
1027///   `version = env!("CARGO_PKG_VERSION")`.
1028/// * `url = <expr>` - The URL for `GetInfo` response. Defaults to empty string.
1029///
1030/// ## On methods:
1031///
1032/// * `#[zlink(interface = "...")]` - Set the interface name for this and subsequent methods. If an
1033///   interface is specified at the impl block level, this overrides it for the current method.
1034/// * `#[zlink(interface = "...", types = [Type1, ...])]` - Set the interface and associate custom
1035///   types with it. This scopes the types to the specified interface so they only appear in that
1036///   interface's introspection output. Particularly useful for multi-interface services.
1037/// * `#[zlink(rename = "MethodName")]` - Custom Varlink method name.
1038///
1039/// ## On parameters:
1040///
1041/// * `#[zlink(rename = "paramName")]` - Custom serialized parameter name. **Required** for
1042///   parameter names starting with `_` (the Rust convention for unused parameters), since such
1043///   names are not valid in the Varlink IDL.
1044/// * `#[zlink(connection)]` - Mark this parameter to receive a mutable reference to the connection.
1045///   This is useful for accessing peer credentials or other connection-specific functionality.
1046///   **Requires an explicit generic socket type parameter** (e.g., `impl<Sock> MyService`).
1047///
1048/// # Compile-time Type Checking
1049///
1050/// The macro verifies at compile time that any custom type referenced in a method's input *or*
1051/// output parameters is declared in `types = [...]`. If you forget to list a type, you'll get
1052/// a compile error:
1053///
1054/// ```rust,compile_fail
1055/// use serde::{Deserialize, Serialize};
1056/// use zlink::{introspect::CustomType, service};
1057///
1058/// #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
1059/// struct Book { title: String }
1060///
1061/// #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
1062/// struct BookList { books: Vec<Book> }
1063///
1064/// struct LibraryService;
1065///
1066/// // Missing `BookList` in `types = [...]` even though it's returned by `list_books`.
1067/// #[service(interface = "org.example.library", types = [Book])]
1068/// impl LibraryService {
1069///     async fn list_books(&self) -> BookList {
1070///         BookList { books: vec![] }
1071///     }
1072/// }
1073/// ```
1074///
1075/// Similarly, parameter names starting with `_` are rejected because they are not valid in the
1076/// Varlink IDL. Specify the wire name explicitly with `#[zlink(rename = "...")]`.
1077///
1078/// # Generated Code
1079///
1080/// The macro generates an `impl<Sock: Socket> Service<Sock> for YourType` with the `handle` method,
1081/// along with internal helper types for serialization/deserialization.
1082///
1083/// # Error Handling
1084///
1085/// Methods can return `Result<T, E>` with any error type `E` that implements `Serialize` and
1086/// `Debug`. Different methods can use different error types - the macro automatically generates
1087/// internal wrapper types to handle all unique error types.
1088///
1089/// When a method returns `Err(e)`, the macro generates code that wraps it in the appropriate
1090/// combo enum variant and returns `MethodReply::Error(...)`.
1091/// When a method returns `Ok(v)`, it returns `MethodReply::Single(Some(v))`.
1092///
1093/// Methods can also return plain values (not wrapped in Result) - these are always treated as
1094/// successful responses.
1095///
1096/// # Custom Socket Bounds
1097///
1098/// By default, the generated `Service` impl uses a generic socket parameter with just the `Socket`
1099/// bound. If you need additional bounds (e.g., for peer credential checking), you can provide your
1100/// own generics on the impl block:
1101///
1102/// ```rust
1103/// use zlink::{service, connection::socket::FetchPeerCredentials, introspect};
1104///
1105/// struct MyService;
1106///
1107/// #[derive(Debug, Clone, PartialEq, zlink::ReplyError, introspect::ReplyError)]
1108/// #[zlink(interface = "org.example.service")]
1109/// enum MyError {
1110///     ServiceError,
1111/// }
1112///
1113/// #[service]
1114/// impl<Sock> MyService
1115/// where
1116///     Sock::ReadHalf: FetchPeerCredentials,
1117/// {
1118///     #[zlink(interface = "org.example.service")]
1119///     async fn get_status(&self) -> Result<(), MyError> {
1120///         Ok(())
1121///     }
1122/// }
1123/// ```
1124///
1125/// The first type parameter is used as the socket type for the generated `Service` impl. The
1126/// `Socket` bound is automatically added, so you only need to specify additional bounds.
1127///
1128/// # Connection Parameter
1129///
1130/// Methods can receive a mutable reference to the connection using `#[zlink(connection)]`:
1131///
1132/// ```rust
1133/// use zlink::{service, Connection, connection::socket::FetchPeerCredentials, introspect};
1134///
1135/// struct MyService;
1136///
1137/// #[derive(Debug, Clone, PartialEq, zlink::ReplyError, introspect::ReplyError)]
1138/// #[zlink(interface = "org.example.service")]
1139/// enum MyError {
1140///     ServiceError,
1141/// }
1142///
1143/// #[service]
1144/// impl<Sock> MyService
1145/// where
1146///     Sock::ReadHalf: FetchPeerCredentials,
1147/// {
1148///     #[zlink(interface = "org.example.service")]
1149///     async fn check_credentials(
1150///         &self,
1151///         #[zlink(connection)] conn: &mut Connection<Sock>,
1152///     ) -> Result<(), MyError> {
1153///         let _creds = conn.peer_credentials().await;
1154///         Ok(())
1155///     }
1156/// }
1157/// ```
1158///
1159/// Methods with connection parameters are only callable through the `Service` trait (not directly
1160/// on the type), since they require the socket type to be known.
1161///
1162/// # Streaming Methods
1163///
1164/// Methods that send multiple replies (the Varlink `more` flag) are marked with `#[zlink(more)]`.
1165/// Such a method must:
1166///
1167/// - Take `more: bool` as the first parameter after `self`. This receives the value of the call's
1168///   `more` flag, allowing the method to behave differently when the client only wants a single
1169///   reply.
1170/// - Return `impl Stream<Item = Reply<T>>` (or `impl Stream<Item = (Reply<T>, Vec<OwnedFd>)>` when
1171///   combined with `#[zlink(return_fds)]`). A concrete stream type is also accepted; in that case
1172///   the macro infers `Reply<T>` from the type's first generic parameter.
1173/// - Set `Reply::set_continues(Some(true))` on every intermediate item and `Some(false)` on the
1174///   final one so that the client knows when the stream ends.
1175///
1176/// ## Returning Method Errors From Streams
1177///
1178/// Streaming methods can also opt in to emitting Varlink error replies as stream items. To do so,
1179/// return a stream whose item is `Result<Reply<T>, E>` (or `(Result<Reply<T>, E>, Vec<OwnedFd>)`
1180/// for `#[zlink(return_fds)]`). When the stream yields `Err(e)`, the server sends an error reply
1181/// to the client. The error type `E` must implement `serde::Serialize` and `Debug`, just like for
1182/// non-streaming methods, and (because the stream outlives `&self`) cannot borrow from the
1183/// service. Different streaming methods may use different error types; the macro combines them
1184/// into a single internal enum exposed as `Service::ReplyStreamError`.
1185///
1186/// Note that, on the wire, an error reply terminates the stream — a client that receives an
1187/// error reply should not expect any further items. The macro does not enforce this on the
1188/// server side, so callers can still drain the rest of the stream if the server emits more items
1189/// after an error.
1190///
1191/// ## Example
1192///
1193/// ```rust
1194/// use futures_util::Stream;
1195/// use serde::{Deserialize, Serialize};
1196/// use zlink::{introspect::Type, service, Reply};
1197///
1198/// #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Type)]
1199/// struct Tick {
1200///     value: u32,
1201/// }
1202///
1203/// struct Counter;
1204///
1205/// #[service(interface = "org.example.counter")]
1206/// impl Counter {
1207///     // Streams `Tick` values from 1 to `to`. If the caller did not set the `more` flag, only a
1208///     // single tick is emitted.
1209///     #[zlink(more)]
1210///     async fn count(
1211///         &self,
1212///         more: bool,
1213///         to: u32,
1214///     ) -> impl Stream<Item = Reply<Tick>> + Unpin {
1215///         let to = if more { to.max(1) } else { 1 };
1216///         futures_util::stream::iter((1..=to).map(move |value| {
1217///             Reply::new(Some(Tick { value })).set_continues(Some(value < to))
1218///         }))
1219///     }
1220/// }
1221/// ```
1222///
1223/// ## Streaming With Errors
1224///
1225/// ```rust
1226/// use futures_util::Stream;
1227/// use serde::{Deserialize, Serialize};
1228/// use zlink::{introspect, service, Reply};
1229///
1230/// #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, introspect::Type)]
1231/// struct Tick { value: u32 }
1232///
1233/// #[derive(Debug, Clone, PartialEq, zlink::ReplyError, introspect::ReplyError)]
1234/// #[zlink(interface = "org.example.counter")]
1235/// enum CountError {
1236///     AtZero,
1237/// }
1238///
1239/// struct Counter;
1240///
1241/// #[service(interface = "org.example.counter")]
1242/// impl Counter {
1243///     #[zlink(more)]
1244///     async fn count(
1245///         &self,
1246///         _more: bool,
1247///         to: u32,
1248///     ) -> impl Stream<Item = Result<Reply<Tick>, CountError>> + Unpin {
1249///         if to == 0 {
1250///             return futures_util::stream::iter(vec![Err(CountError::AtZero)]);
1251///         }
1252///         let last = to;
1253///         let items: Vec<Result<Reply<Tick>, CountError>> = (1..=to)
1254///             .map(move |value| {
1255///                 Ok(Reply::new(Some(Tick { value })).set_continues(Some(value < last)))
1256///             })
1257///             .collect();
1258///         futures_util::stream::iter(items)
1259///     }
1260/// }
1261/// ```
1262///
1263/// # Example
1264///
1265/// ```rust
1266/// use zlink::{
1267///     introspect::{self, Type},
1268///     service,
1269///     unix::{bind, connect},
1270///     Server,
1271/// };
1272/// use serde::{Deserialize, Serialize};
1273///
1274/// // Response type for balance operations.
1275/// #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Type)]
1276/// struct Balance {
1277///     amount: i64,
1278/// }
1279///
1280/// // Error type - must derive zlink::ReplyError for proper serialization.
1281/// #[derive(Debug, Clone, PartialEq, zlink::ReplyError, introspect::ReplyError)]
1282/// #[zlink(interface = "org.example.bank")]
1283/// enum BankError {
1284///     InsufficientFunds { available: i64, requested: i64 },
1285///     InvalidAmount { amount: i64 },
1286///     AccountLocked,
1287/// }
1288///
1289/// struct BankAccount {
1290///     balance: i64,
1291///     locked: bool,
1292/// }
1293///
1294/// impl BankAccount {
1295///     fn new(initial_balance: i64) -> Self {
1296///         Self { balance: initial_balance, locked: false }
1297///     }
1298/// }
1299///
1300/// // Service implementation with error handling.
1301/// #[service]
1302/// impl BankAccount {
1303///     // Method that returns a plain value (not Result) - always succeeds.
1304///     #[zlink(interface = "org.example.bank")]
1305///     async fn get_balance(&self) -> Balance {
1306///         Balance { amount: self.balance }
1307///     }
1308///
1309///     // Method that can fail - returns Result<Balance, BankError>.
1310///     async fn deposit(&mut self, amount: i64) -> Result<Balance, BankError> {
1311///         if self.locked {
1312///             return Err(BankError::AccountLocked);
1313///         }
1314///         if amount <= 0 {
1315///             return Err(BankError::InvalidAmount { amount });
1316///         }
1317///         self.balance += amount;
1318///         Ok(Balance { amount: self.balance })
1319///     }
1320///
1321///     async fn withdraw(&mut self, amount: i64) -> Result<Balance, BankError> {
1322///         if self.locked {
1323///             return Err(BankError::AccountLocked);
1324///         }
1325///         if amount <= 0 {
1326///             return Err(BankError::InvalidAmount { amount });
1327///         }
1328///         if amount > self.balance {
1329///             return Err(BankError::InsufficientFunds {
1330///                 available: self.balance,
1331///                 requested: amount,
1332///             });
1333///         }
1334///         self.balance -= amount;
1335///         Ok(Balance { amount: self.balance })
1336///     }
1337///
1338///     // Method returning Result<(), E> - void success, can fail.
1339///     async fn lock_account(&mut self) -> Result<(), BankError> {
1340///         if self.locked {
1341///             return Err(BankError::AccountLocked);
1342///         }
1343///         self.locked = true;
1344///         Ok(())
1345///     }
1346/// }
1347///
1348/// // Client-side proxy definition.
1349/// #[zlink::proxy("org.example.bank")]
1350/// trait BankProxy {
1351///     async fn get_balance(&mut self) -> zlink::Result<Result<Balance, BankError>>;
1352///     async fn deposit(&mut self, amount: i64) -> zlink::Result<Result<Balance, BankError>>;
1353///     async fn withdraw(&mut self, amount: i64) -> zlink::Result<Result<Balance, BankError>>;
1354///     async fn lock_account(&mut self) -> zlink::Result<Result<(), BankError>>;
1355/// }
1356///
1357/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1358/// // Server setup.
1359/// let dir = tempfile::tempdir()?;
1360/// let socket_path = dir.path().join("service-example.sock");
1361/// let listener = bind(&socket_path)?;
1362/// let service = BankAccount::new(1000);
1363/// let server = Server::new(listener, service);
1364///
1365/// // Run server and client concurrently.
1366/// tokio::select! {
1367///     res = server.run() => res?,
1368///     res = async {
1369///         let mut conn = connect(&socket_path).await?;
1370///
1371///         // Check initial balance.
1372///         let balance = conn.get_balance().await?.unwrap();
1373///         assert_eq!(balance.amount, 1000);
1374///
1375///         // Successful deposit.
1376///         let balance = conn.deposit(500).await?.unwrap();
1377///         assert_eq!(balance.amount, 1500);
1378///
1379///         // Successful withdrawal.
1380///         let balance = conn.withdraw(200).await?.unwrap();
1381///         assert_eq!(balance.amount, 1300);
1382///
1383///         // Error: withdraw more than available.
1384///         let err = conn.withdraw(5000).await?.unwrap_err();
1385///         assert_eq!(err, BankError::InsufficientFunds { available: 1300, requested: 5000 });
1386///
1387///         // Error: invalid amount.
1388///         let err = conn.deposit(-100).await?.unwrap_err();
1389///         assert_eq!(err, BankError::InvalidAmount { amount: -100 });
1390///
1391///         // Lock account and verify subsequent operations fail.
1392///         conn.lock_account().await?.unwrap();
1393///         let err = conn.withdraw(100).await?.unwrap_err();
1394///         assert_eq!(err, BankError::AccountLocked);
1395///
1396///         Ok::<(), Box<dyn std::error::Error>>(())
1397///     } => res?,
1398/// }
1399/// # Ok::<(), Box<dyn std::error::Error>>(())
1400/// # })?;
1401/// # Ok::<(), Box<dyn std::error::Error>>(())
1402/// ```
1403///
1404/// # Introspection Example
1405///
1406/// The service automatically provides introspection via the `org.varlink.service` interface:
1407///
1408/// ```rust
1409/// use zlink::{
1410///     introspect::{self, CustomType, Type},
1411///     service,
1412///     varlink_service::Proxy,
1413/// };
1414/// use serde::{Deserialize, Serialize};
1415///
1416/// // Custom type - must derive CustomType to be included in IDL.
1417/// #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
1418/// struct Balance {
1419///     amount: i64,
1420/// }
1421///
1422/// #[derive(Debug, Clone, PartialEq, zlink::ReplyError, introspect::ReplyError)]
1423/// #[zlink(interface = "org.example.bank")]
1424/// enum BankError {
1425///     InsufficientFunds { available: i64 },
1426/// }
1427///
1428/// struct BankService;
1429///
1430/// // Include custom types in the service for IDL generation.
1431/// #[service(
1432///     types = [Balance],
1433///     vendor = "Example Corp",
1434///     product = "Bank Service",
1435///     version = env!("CARGO_PKG_VERSION"),
1436///     url = "https://example.com/bank"
1437/// )]
1438/// impl BankService {
1439///     #[zlink(interface = "org.example.bank")]
1440///     async fn get_balance(&self) -> Result<Balance, BankError> {
1441///         Ok(Balance { amount: 1000 })
1442///     }
1443/// }
1444///
1445/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1446/// # use zlink::test_utils::mock_socket::MockSocket;
1447/// // GetInfo returns metadata and list of interfaces.
1448/// # let response = format!(
1449/// #     r#"{{"parameters":{{"vendor":"Example Corp","product":"Bank Service","version":"{}","url":"https://example.com/bank","interfaces":["org.example.bank","org.varlink.service"]}}}}"#,
1450/// #     env!("CARGO_PKG_VERSION"),
1451/// # );
1452/// # let socket = MockSocket::with_responses(&[&response]);
1453/// # let mut conn = zlink::Connection::new(socket);
1454/// let info = conn.get_info().await?.unwrap();
1455/// assert_eq!(info.vendor, "Example Corp");
1456/// let interfaces: Vec<&str> = info.interfaces.iter().map(|s| s.as_ref()).collect();
1457/// assert_eq!(interfaces.as_slice(), ["org.example.bank", "org.varlink.service"]);
1458/// # Ok::<(), Box<dyn std::error::Error>>(())
1459/// # }).unwrap();
1460///
1461/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1462/// # use zlink::test_utils::mock_socket::MockSocket;
1463/// // GetInterfaceDescription returns the IDL, which can be parsed to verify methods and types.
1464/// # let responses = [
1465/// #     r#"{"parameters":{"description":"interface org.example.bank\n\ntype Balance (amount: int)\n\nmethod GetBalance() -> (amount: int)\n\nerror InsufficientFunds (available: int)"}}"#,
1466/// # ];
1467/// # let socket = MockSocket::with_responses(&responses);
1468/// # let mut conn = zlink::Connection::new(socket);
1469/// let desc = conn.get_interface_description("org.example.bank").await?.unwrap();
1470/// let interface = desc.parse()?;
1471/// assert_eq!(interface.name(), "org.example.bank");
1472///
1473/// // Verify methods are present.
1474/// let method_names: Vec<_> = interface.methods().map(|m| m.name()).collect();
1475/// assert_eq!(method_names.as_slice(), ["GetBalance"]);
1476///
1477/// // Verify custom types are included.
1478/// let type_names: Vec<_> = interface.custom_types().map(|t| t.name()).collect();
1479/// assert_eq!(type_names.as_slice(), ["Balance"]);
1480///
1481/// // Verify errors are present.
1482/// let error_names: Vec<_> = interface.errors().map(|e| e.name()).collect();
1483/// assert_eq!(error_names.as_slice(), ["InsufficientFunds"]);
1484/// # Ok::<(), Box<dyn std::error::Error>>(())
1485/// # }).unwrap();
1486/// ```
1487///
1488/// # Method Name Conversion
1489///
1490/// By default, method names are converted from snake_case to PascalCase for the Varlink call.
1491/// For example, `get_balance` becomes `GetBalance`. Use `#[zlink(rename = "...")]` to override
1492/// this.
1493///
1494/// # Full Method Path
1495///
1496/// The full Varlink method path is constructed as `{interface}.{MethodName}`. For example,
1497/// if the interface is `org.example.bank` and the method is `GetBalance`, the full path
1498/// will be `org.example.bank.GetBalance`.
1499///
1500/// # Interface Propagation
1501///
1502/// The interface for methods is determined in this order:
1503/// 1. If the method has `#[zlink(interface = "...")]`, that interface is used.
1504/// 2. Otherwise, the interface is inherited from the previous method or from the macro-level
1505///    `interface = "..."` attribute.
1506///
1507/// For services implementing a single interface, specifying `interface = "..."` at the macro level
1508/// is the simplest approach - all methods automatically use that interface without needing
1509/// individual attributes.
1510#[cfg(feature = "service")]
1511#[proc_macro_attribute]
1512pub fn service(
1513    attr: proc_macro::TokenStream,
1514    input: proc_macro::TokenStream,
1515) -> proc_macro::TokenStream {
1516    service::service(attr.into(), input.into()).into()
1517}