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