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/// Derives `Type` for structs and enums, generating appropriate `Type::Object` or `Type::Enum`
24/// representation.
25///
26/// **Requires the `introspection` feature to be enabled.**
27///
28/// ## Structs
29///
30/// For structs, this macro supports named fields and unit structs. It will generate a
31/// `Type` implementation that creates a `Type::Object` containing all the fields with their
32/// names and types. Tuple structs are not supported as Varlink does not support unnamed fields.
33///
34/// ## Enums
35///
36/// For enums, this macro only supports unit variants (variants without associated data). It will
37/// generate a `Type` implementation that creates a `Type::Enum` containing all the variant
38/// names.
39///
40/// # Supported Attributes
41///
42/// The following attributes can be used to customize the behavior of this derive macro:
43///
44/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
45///   `::zlink`.
46///
47/// # Limitations
48///
49/// The following types are **not** supported by this macro:
50///
51/// - **Tuple structs**: Varlink does not support unnamed fields
52/// - **Enums with data**: Only unit enums (variants without associated data) are supported
53/// - **Unions**: Not supported by Varlink
54///
55/// ```rust,compile_fail
56/// # use zlink::introspect::Type;
57/// #[derive(Type)]  // This will fail to compile
58/// struct Point(f32, f32, f32);
59/// ```
60///
61/// ```rust,compile_fail
62/// # use zlink::introspect::Type;
63/// #[derive(Type)]  // This will fail to compile
64/// enum Status {
65///     Active(String),  // Variants with data are not supported
66///     Inactive,
67/// }
68/// ```
69///
70/// # Examples
71///
72/// ## Named Structs
73///
74/// ```rust
75/// use zlink::introspect::Type;
76/// use zlink::idl;
77///
78/// #[derive(Type)]
79/// struct Person {
80///     name: String,
81///     age: i32,
82///     active: bool,
83/// }
84///
85/// // Access the generated type information
86/// match Person::TYPE {
87///     idl::Type::Object(fields) => {
88///         let field_vec: Vec<_> = fields.iter().collect();
89///         assert_eq!(field_vec.len(), 3);
90///
91///         assert_eq!(field_vec[0].name(), "name");
92///         assert_eq!(field_vec[0].ty(), &idl::Type::String);
93///
94///         assert_eq!(field_vec[1].name(), "age");
95///         assert_eq!(field_vec[1].ty(), &idl::Type::Int);
96///
97///         assert_eq!(field_vec[2].name(), "active");
98///         assert_eq!(field_vec[2].ty(), &idl::Type::Bool);
99///     }
100///     _ => panic!("Expected struct type"),
101/// }
102/// ```
103///
104/// ## Unit Structs
105///
106/// ```rust
107/// # use zlink::introspect::Type;
108/// # use zlink::idl;
109/// #[derive(Type)]
110/// struct Unit;
111///
112/// // Unit structs generate empty field lists
113/// match Unit::TYPE {
114///     idl::Type::Object(fields) => {
115///         assert_eq!(fields.len(), 0);
116///     }
117///     _ => panic!("Expected struct type"),
118/// }
119/// ```
120///
121/// ## Complex Types
122///
123/// ```rust
124/// # use zlink::introspect::Type;
125/// # use zlink::idl;
126/// #[derive(Type)]
127/// struct Complex {
128///     id: u64,
129///     description: Option<String>,
130///     tags: Vec<String>,
131/// }
132///
133/// // The macro handles nested types like Option<T> and Vec<T>
134/// match Complex::TYPE {
135///     idl::Type::Object(fields) => {
136///         let field_vec: Vec<_> = fields.iter().collect();
137///
138///         // Optional field becomes Type::Optional
139///         match field_vec[1].ty() {
140///             idl::Type::Optional(inner) => assert_eq!(inner.inner(), &idl::Type::String),
141///             _ => panic!("Expected optional type"),
142///         }
143///
144///         // Vec field becomes Type::Array
145///         match field_vec[2].ty() {
146///             idl::Type::Array(inner) => assert_eq!(inner.inner(), &idl::Type::String),
147///             _ => panic!("Expected array type"),
148///         }
149///     }
150///     _ => panic!("Expected struct type"),
151/// }
152/// ```
153///
154/// ## Unit Enums
155///
156/// ```rust
157/// # use zlink::introspect::Type;
158/// # use zlink::idl;
159/// #[derive(Type)]
160/// enum Status {
161///     Active,
162///     Inactive,
163///     Pending,
164/// }
165///
166/// // Unit enums generate variant lists
167/// match Status::TYPE {
168///     idl::Type::Enum(variants) => {
169///         let variant_vec: Vec<_> = variants.iter().collect();
170///         assert_eq!(variant_vec.len(), 3);
171///         assert_eq!(variant_vec[0].name(), "Active");
172///         assert_eq!(variant_vec[1].name(), "Inactive");
173///         assert_eq!(variant_vec[2].name(), "Pending");
174///     }
175///     _ => panic!("Expected enum type"),
176/// }
177/// ```
178#[cfg(feature = "introspection")]
179#[proc_macro_derive(IntrospectType, attributes(zlink))]
180pub fn derive_introspect_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
181    introspect::r#type::derive_type(input)
182}
183
184/// Derives `Type` for structs and enums, generating named custom type definitions.
185///
186/// **Requires the `introspection` feature to be enabled.**
187///
188/// This macro generates implementations of the `CustomType` trait, which provides named
189/// custom type definitions suitable for IDL generation. It also generates a `Type` implementation
190/// and therefore is mutually exclusive to `zlink::introspect::Type` derive macro.
191///
192/// ## Structs
193///
194/// For structs, this macro generates a `custom::Type::Object` containing the struct name and
195/// all fields with their names and types.
196///
197/// ## Enums
198///
199/// For enums, this macro only supports unit variants and generates a `custom::Type::Enum`
200/// containing the enum name and all variant names.
201///
202/// # Supported Attributes
203///
204/// The following attributes can be used to customize the behavior of this derive macro:
205///
206/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
207///   `::zlink`.
208///
209/// # Examples
210///
211/// ## Named Structs
212///
213/// ```rust
214/// use zlink::introspect::{CustomType, Type};
215/// use zlink::idl;
216///
217/// #[derive(CustomType)]
218/// struct Point {
219///     x: f64,
220///     y: f64,
221/// }
222///
223/// // Access the generated custom type information
224/// match Point::CUSTOM_TYPE {
225///     idl::CustomType::Object(obj) => {
226///         assert_eq!(obj.name(), "Point");
227///         let fields: Vec<_> = obj.fields().collect();
228///         assert_eq!(fields.len(), 2);
229///         assert_eq!(fields[0].name(), "x");
230///         assert_eq!(fields[1].name(), "y");
231///     }
232///     _ => panic!("Expected custom object type"),
233/// }
234///
235/// match Point::TYPE {
236///     idl::Type::Custom(name) => {
237///         assert_eq!(*name, "Point");
238///     }
239///     _ => panic!("Expected custom type"),
240/// }
241/// ```
242///
243/// ## Unit Enums
244///
245/// ```rust
246/// # use zlink::introspect::{CustomType, Type};
247/// # use zlink::idl;
248/// #[derive(CustomType)]
249/// enum Status {
250///     Active,
251///     Inactive,
252///     Pending,
253/// }
254///
255/// // Access the generated custom enum type information
256/// match Status::CUSTOM_TYPE {
257///     idl::CustomType::Enum(enm) => {
258///         assert_eq!(enm.name(), "Status");
259///         let variants: Vec<_> = enm.variants().collect();
260///         assert_eq!(variants.len(), 3);
261///         assert_eq!(variants[0].name(), "Active");
262///         assert_eq!(variants[1].name(), "Inactive");
263///         assert_eq!(variants[2].name(), "Pending");
264///     }
265///     _ => panic!("Expected custom enum type"),
266/// }
267///
268/// match Status::TYPE {
269///     idl::Type::Custom(name) => {
270///         assert_eq!(*name, "Status");
271///     }
272///     _ => panic!("Expected custom type"),
273/// }
274/// ```
275#[cfg(feature = "introspection")]
276#[proc_macro_derive(IntrospectCustomType, attributes(zlink))]
277pub fn derive_introspect_custom_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
278    introspect::custom_type::derive_custom_type(input)
279}
280
281/// Derives `ReplyError` for enums, generating error definitions for Varlink service errors.
282///
283/// **Requires the `introspection` feature to be enabled.**
284///
285/// This macro generates implementations of the `ReplyError` trait, which provides a list of
286/// error variants that can be returned by a Varlink service method. It supports unit variants,
287/// variants with named fields, and single-field tuple variants (where the field type implements
288/// `Type` and has a `Type::Object`).
289///
290/// # Supported Attributes
291///
292/// The following attributes can be used to customize the behavior of this derive macro:
293///
294/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
295///   `::zlink`.
296///
297/// # Example
298///
299/// ```rust
300/// use zlink::introspect::ReplyError;
301///
302/// #[derive(ReplyError)]
303/// enum ServiceError {
304///     // Unit variant - no parameters
305///     NotFound,
306///
307///     // Named field variant - multiple parameters
308///     InvalidQuery {
309///         message: String,
310///         line: u32,
311///     },
312///
313///     // Single tuple variant - uses fields from the wrapped type
314///     ValidationFailed(ValidationDetails),
315/// }
316///
317/// // Example struct for tuple variant
318/// #[derive(zlink::introspect::Type)]
319/// struct ValidationDetails {
320///     field_name: String,
321///     expected: String,
322/// }
323///
324/// // Access the generated error variants
325/// assert_eq!(ServiceError::VARIANTS.len(), 3);
326/// assert_eq!(ServiceError::VARIANTS[0].name(), "NotFound");
327/// assert!(ServiceError::VARIANTS[0].has_no_fields());
328///
329/// assert_eq!(ServiceError::VARIANTS[1].name(), "InvalidQuery");
330/// assert!(!ServiceError::VARIANTS[1].has_no_fields());
331/// ```
332#[cfg(feature = "introspection")]
333#[proc_macro_derive(IntrospectReplyError, attributes(zlink))]
334pub fn derive_introspect_reply_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
335    introspect::reply_error::derive_reply_error(input)
336}
337
338/// Creates a client-side proxy for calling Varlink methods on a connection.
339///
340/// **Requires the `proxy` feature to be enabled.**
341///
342/// This attribute macro generates an implementation of the provided trait for `Connection<S>`,
343/// automatically handling the serialization of method calls and deserialization of responses.
344/// Each proxy trait targets a single Varlink interface.
345///
346/// The macro also generates a chain extension trait that allows you to chain multiple method
347/// calls together for efficient batching across multiple interfaces.
348///
349/// # Supported Attributes
350///
351/// The following attributes can be used to customize the behavior of this macro:
352///
353/// * `interface` (required) - The Varlink interface name (e.g., `"org.varlink.service"`).
354/// * `crate` - Specifies the crate path to use for zlink types. Defaults to `::zlink`.
355/// * `chain_name` - Custom name for the generated chain extension trait. Defaults to
356///   `{TraitName}Chain`.
357///
358/// # Example
359///
360/// ```rust
361/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
362/// use zlink::proxy;
363/// use serde::{Deserialize, Serialize};
364/// use serde_prefix_all::prefix_all;
365/// use futures_util::stream::Stream;
366///
367/// #[proxy("org.example.MyService")]
368/// trait MyServiceProxy {
369///     // Non-streaming methods can use borrowed types.
370///     async fn get_status(&mut self) -> zlink::Result<Result<Status<'_>, MyError<'_>>>;
371///     async fn set_value(
372///         &mut self,
373///         key: &str,
374///         value: i32,
375///     ) -> zlink::Result<Result<(), MyError<'_>>>;
376///     #[zlink(rename = "ListMachines")]
377///     async fn list_machines(&mut self) -> zlink::Result<Result<Vec<Machine<'_>>, MyError<'_>>>;
378///     // Streaming methods must use owned types (DeserializeOwned) because the internal buffer may
379///     // be reused between stream iterations.
380///     #[zlink(rename = "GetStatus", more)]
381///     async fn stream_status(
382///         &mut self,
383///     ) -> zlink::Result<
384///         impl Stream<Item = zlink::Result<Result<OwnedStatus, OwnedMyError>>>,
385///     >;
386/// }
387///
388/// // The macro generates:
389/// // impl<S: Socket> MyServiceProxy for Connection<S> { ... }
390///
391/// // Borrowed types for non-streaming methods.
392/// #[derive(Debug, Serialize, Deserialize)]
393/// struct Status<'m> {
394///     active: bool,
395///     message: &'m str,
396/// }
397///
398/// #[derive(Debug, Serialize, Deserialize)]
399/// struct Machine<'m> { name: &'m str }
400///
401/// #[prefix_all("org.example.MyService.")]
402/// #[derive(Debug, Serialize, Deserialize)]
403/// #[serde(tag = "error", content = "parameters")]
404/// enum MyError<'a> {
405///     NotFound,
406///     InvalidRequest,
407///     // Parameters must be named.
408///     CodedError { code: u32, message: &'a str },
409/// }
410///
411/// // Owned types for streaming methods (required by the `more` attribute).
412/// #[derive(Debug, Serialize, Deserialize)]
413/// struct OwnedStatus {
414///     active: bool,
415///     message: String,
416/// }
417///
418/// #[prefix_all("org.example.MyService.")]
419/// #[derive(Debug, Serialize, Deserialize)]
420/// #[serde(tag = "error", content = "parameters")]
421/// enum OwnedMyError {
422///     NotFound,
423///     InvalidRequest,
424///     CodedError { code: u32, message: String },
425/// }
426///
427/// // Example usage:
428/// # use zlink::test_utils::mock_socket::MockSocket;
429/// # let responses = vec![
430/// #     r#"{"parameters":{"active":true,"message":"System running"}}"#,
431/// # ];
432/// # let socket = MockSocket::with_responses(&responses);
433/// # let mut conn = zlink::Connection::new(socket);
434/// let result = conn.get_status().await?.unwrap();
435/// assert_eq!(result.active, true);
436/// assert_eq!(result.message, "System running");
437/// # Ok::<(), Box<dyn std::error::Error>>(())
438/// # }).unwrap();
439/// ```
440///
441/// # Chaining Method Calls
442///
443/// The proxy macro generates chain extension traits that allow you to batch multiple method calls
444/// together. This is useful for reducing round trips and efficiently calling methods across
445/// multiple interfaces. Each method gets a `chain_` prefixed variant that starts a chain.
446///
447/// **Important**: Chain methods are only generated for proxy methods that use owned types
448/// (`DeserializeOwned`) in their return type. Methods with borrowed types (non-static lifetimes)
449/// don't get chain variants since the internal buffer may be reused between stream iterations.
450/// Input arguments can still use borrowed types.
451///
452/// ## Example: Chaining Method Calls
453///
454/// ```rust
455/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
456/// # use zlink::proxy;
457/// # use serde::{Deserialize, Serialize};
458/// # use futures_util::{pin_mut, TryStreamExt};
459/// #
460/// // Owned reply types for chain API.
461/// # #[derive(Debug, Serialize, Deserialize)]
462/// # struct User { id: u64, name: String }
463/// # #[derive(Debug, Serialize, Deserialize)]
464/// # struct Post { id: u64, user_id: u64, content: String }
465/// # #[derive(Debug, Serialize, Deserialize)]
466/// # #[serde(untagged)]
467/// # enum BlogReply {
468/// #     User(User),
469/// #     Post(Post),
470/// #     Posts(Vec<Post>)
471/// # }
472/// # #[derive(Debug, Serialize, Deserialize)]
473/// # #[serde(tag = "error")]
474/// # enum BlogError { NotFound, InvalidInput }
475/// # impl std::fmt::Display for BlogError {
476/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
477/// #         match self {
478/// #             Self::NotFound => write!(f, "Not found"),
479/// #             Self::InvalidInput => write!(f, "Invalid input")
480/// #         }
481/// #     }
482/// # }
483/// # impl std::error::Error for BlogError {}
484/// #
485/// // Define proxies with owned return types - chain methods are generated.
486/// #[proxy("org.example.blog.Users")]
487/// trait UsersProxy {
488///     async fn get_user(&mut self, id: u64)
489///         -> zlink::Result<Result<BlogReply, BlogError>>;
490///     async fn create_user(&mut self, name: &str)
491///         -> zlink::Result<Result<BlogReply, BlogError>>;
492/// }
493///
494/// #[proxy("org.example.blog.Posts")]
495/// trait PostsProxy {
496///     async fn get_posts_by_user(&mut self, user_id: u64)
497///         -> zlink::Result<Result<BlogReply, BlogError>>;
498///     async fn create_post(&mut self, user_id: u64, content: &str)
499///         -> zlink::Result<Result<BlogReply, BlogError>>;
500/// }
501///
502/// # use zlink::test_utils::mock_socket::MockSocket;
503/// # let responses = vec![
504/// #     r#"{"parameters":{"id":1,"name":"Alice"}}"#,
505/// #     r#"{"parameters":{"id":1,"user_id":1,"content":"My first post!"}}"#,
506/// #     r#"{"parameters":[{"id":1,"user_id":1,"content":"My first post!"}]}"#,
507/// #     r#"{"parameters":{"id":1,"name":"Alice"}}"#,
508/// # ];
509/// # let socket = MockSocket::with_responses(&responses);
510/// # let mut conn = zlink::Connection::new(socket);
511/// let chain = conn
512///     .chain_create_user("Alice")?
513///     .create_post(1, "My first post!")?
514///     .get_posts_by_user(1)?
515///     .get_user(1)?;
516///
517/// // Send all calls in a single batch.
518/// let replies = chain.send::<BlogReply, BlogError>().await?;
519/// pin_mut!(replies);
520///
521/// // Process replies in order.
522/// let mut reply_count = 0;
523/// while let Some((reply, _fds)) = replies.try_next().await? {
524///     reply_count += 1;
525///     if let Ok(response) = reply {
526///         match response.parameters() {
527///             Some(BlogReply::User(user)) => assert_eq!(user.name, "Alice"),
528///             Some(BlogReply::Post(post)) => assert_eq!(post.content, "My first post!"),
529///             Some(BlogReply::Posts(posts)) => assert_eq!(posts.len(), 1),
530///             None => {} // set_value returns empty response
531///         }
532///     }
533/// }
534/// assert_eq!(reply_count, 4); // We made 4 calls
535/// # Ok::<(), Box<dyn std::error::Error>>(())
536/// # }).unwrap();
537/// ```
538///
539/// ## Combining Multiple Services
540///
541/// You can chain calls across multiple custom services. Define a combined reply type that can
542/// deserialize responses from all interfaces:
543///
544/// ```rust
545/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
546/// # use zlink::proxy;
547/// # use serde::{Deserialize, Serialize};
548/// # use futures_util::{pin_mut, TryStreamExt};
549/// #
550/// # #[derive(Debug, Serialize, Deserialize)]
551/// # struct Status { active: bool, message: String }
552/// # #[derive(Debug, Serialize, Deserialize)]
553/// # struct HealthInfo { healthy: bool, uptime: u64 }
554/// # #[derive(Debug, Serialize, Deserialize)]
555/// # #[serde(tag = "error")]
556/// # enum ServiceError { NotFound, InvalidRequest }
557/// # impl std::fmt::Display for ServiceError {
558/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
559/// #         match self {
560/// #             Self::NotFound => write!(f, "Not found"),
561/// #             Self::InvalidRequest => write!(f, "Invalid input")
562/// #         }
563/// #     }
564/// # }
565/// # impl std::error::Error for ServiceError {}
566/// #
567/// // Multiple proxies with owned return types.
568/// #[proxy("com.example.StatusService")]
569/// trait StatusProxy {
570///     async fn get_status(&mut self) -> zlink::Result<Result<Status, ServiceError>>;
571/// }
572///
573/// #[proxy("com.example.HealthService")]
574/// trait HealthProxy {
575///     async fn get_health(&mut self) -> zlink::Result<Result<HealthInfo, ServiceError>>;
576/// }
577///
578/// // Combined reply type for cross-interface chaining.
579/// #[derive(Debug, Deserialize)]
580/// #[serde(untagged)]
581/// enum CombinedReply {
582///     Status(Status),
583///     Health(HealthInfo),
584/// }
585///
586/// # use zlink::test_utils::mock_socket::MockSocket;
587/// # let responses = vec![
588/// #     r#"{"parameters":{"active":true,"message":"Running"}}"#,
589/// #     r#"{"parameters":{"healthy":true,"uptime":12345}}"#,
590/// # ];
591/// # let socket = MockSocket::with_responses(&responses);
592/// # let mut conn = zlink::Connection::new(socket);
593/// // Chain calls across both services.
594/// let chain = conn
595///     .chain_get_status()?
596///     .get_health()?;
597///
598/// let replies = chain.send::<CombinedReply, ServiceError>().await?;
599/// pin_mut!(replies);
600///
601/// let mut count = 0;
602/// while let Some((reply, _fds)) = replies.try_next().await? {
603///     count += 1;
604///     if let Ok(response) = reply {
605///         match response.parameters() {
606///             Some(CombinedReply::Status(s)) => println!("Status: {}", s.message),
607///             Some(CombinedReply::Health(h)) => println!("Uptime: {}", h.uptime),
608///             None => {}
609///         }
610///     }
611/// }
612/// assert_eq!(count, 2);
613/// # Ok::<(), Box<dyn std::error::Error>>(())
614/// # }).unwrap();
615/// ```
616///
617/// ## Chain Extension Traits
618///
619/// For each proxy trait, the macro generates a corresponding chain extension trait. For example,
620/// `FtlProxy` gets `FtlProxyChain`. This trait is automatically implemented for `Chain` types,
621/// allowing seamless method chaining across interfaces.
622///
623/// # Method Requirements
624///
625/// Proxy methods must:
626/// - Take `&mut self` as the first parameter
627/// - Can be either `async fn` or return `impl Future`
628/// - Return `zlink::Result<Result<ReplyType, ErrorType>>` (outer Result for connection errors,
629///   inner for method errors)
630/// - The arguments can be any type that implement `serde::Serialize`
631/// - The reply type (`Ok` case of the inner `Result`) must be a type that implements
632///   `serde::Deserialize` and deserializes itself from a JSON object. Typically you'd just use a
633///   struct that derives `serde::Deserialize`.
634/// - The reply error type (`Err` case of the inner `Result`) must be a type `serde::Deserialize`
635///   that deserializes itself from a JSON object with two fields:
636///   - `error`: a string containing the fully qualified error name
637///   - `parameters`: an optional object containing all the fields of the error
638///
639/// # Method Names
640///
641/// By default, method names are converted from snake_case to PascalCase for the Varlink call.
642/// To specify a different Varlink method name, use the `#[zlink(rename = "...")]` attribute. See
643/// `list_machines` in the example above.
644///
645/// # Streaming Methods
646///
647/// For methods that support streaming (the 'more' flag), use the `#[zlink(more)]` attribute.
648/// Streaming methods must return `Result<impl Stream<Item = Result<Result<ReplyType,
649/// ErrorType>>>>`. The proxy will automatically set the 'more' flag on the call and return a
650/// stream of replies.
651///
652/// # One-way Methods
653///
654/// For fire-and-forget methods that don't expect a reply, use the `#[zlink(oneway)]` attribute.
655/// One-way methods send the call and return immediately without waiting for a response. The method
656/// must return `zlink::Result<()>` (just the outer Result for connection errors, no inner Result
657/// since there's no reply to process).
658///
659/// One-way methods cannot be combined with `#[zlink(more)]` or `#[zlink(return_fds)]`.
660///
661/// This attribute is particularly useful in combination with chaining method calls. When you chain
662/// oneway methods with regular methods, the oneway calls are sent but don't contribute to the reply
663/// stream. For example, if you chain 4 calls where 2 are regular and 2 are oneway, you'll only
664/// receive 2 replies. This allows you to efficiently batch side-effect operations (like resets or
665/// notifications) alongside queries in a single round-trip.
666///
667/// ```rust
668/// # use zlink::proxy;
669/// # use serde::{Deserialize, Serialize};
670/// #[proxy("org.example.Notifications")]
671/// trait NotificationsProxy {
672///     /// Fire-and-forget notification - returns immediately without waiting for a reply.
673///     #[zlink(oneway)]
674///     async fn notify(&mut self, message: &str) -> zlink::Result<()>;
675///
676///     /// Another one-way method with multiple parameters.
677///     #[zlink(oneway)]
678///     async fn log_event(&mut self, level: &str, message: &str, timestamp: u64)
679///         -> zlink::Result<()>;
680/// }
681/// ```
682///
683/// # File Descriptor Passing
684///
685/// **Requires the `std` feature to be enabled.**
686///
687/// Methods can send and receive file descriptors using the following attributes:
688///
689/// ## Sending File Descriptors
690///
691/// Use `#[zlink(fds)]` on a parameter of type `Vec<OwnedFd>` to send file descriptors with the
692/// method call. Only one FD parameter is allowed per method.
693///
694/// ## Receiving File Descriptors
695///
696/// Use `#[zlink(return_fds)]` on a method to indicate it returns file descriptors. The method's
697/// return type must be `Result<(Result<ReplyType, ErrorType>, Vec<OwnedFd>)>` - a tuple containing
698/// both the method result and the received file descriptors. The FDs are available regardless of
699/// whether the method succeeded or failed.
700///
701/// ## Example: File Descriptor Passing
702///
703/// File descriptors are passed out-of-band from the encoded JSON parameters. The typical pattern
704/// is to include integer indexes in your JSON parameters that reference positions in the FD
705/// vector. This is similar to how D-Bus handles FD passing.
706///
707/// ```rust
708/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
709/// use zlink::proxy;
710/// use serde::{Deserialize, Serialize};
711/// use std::os::fd::OwnedFd;
712///
713/// #[proxy("org.example.FileService")]
714/// trait FileServiceProxy {
715///     // Send file descriptors to the service
716///     // The stdin/stdout parameters are indexes into the FDs vector
717///     async fn spawn_process(
718///         &mut self,
719///         command: String,
720///         stdin_fd: u32,
721///         stdout_fd: u32,
722///         #[zlink(fds)] fds: Vec<OwnedFd>,
723///     ) -> zlink::Result<Result<ProcessInfo, FileError>>;
724///
725///     // Receive file descriptors from the service
726///     // Returns metadata with FD indexes and the actual FDs
727///     #[zlink(return_fds)]
728///     async fn open_files(
729///         &mut self,
730///         paths: Vec<String>,
731///     ) -> zlink::Result<(Result<Vec<FileInfo>, FileError>, Vec<OwnedFd>)>;
732/// }
733///
734/// #[derive(Debug, Serialize, Deserialize)]
735/// struct ProcessInfo {
736///     pid: u32,
737/// }
738///
739/// // Response contains FD indexes referencing the FD vector
740/// #[derive(Debug, Serialize, Deserialize)]
741/// struct FileInfo {
742///     path: String,
743///     fd: u32,  // Index into the FD vector (0, 1, 2, etc.)
744/// }
745///
746/// #[derive(Debug, Serialize, Deserialize)]
747/// #[serde(tag = "error")]
748/// enum FileError {
749///     NotFound { path: String },
750///     PermissionDenied { path: String },
751/// }
752/// # impl std::error::Error for FileError {}
753/// # impl std::fmt::Display for FileError {
754/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
755/// #         match self {
756/// #             FileError::NotFound { path } => write!(f, "File not found: {}", path),
757/// #             FileError::PermissionDenied { path } =>
758/// #                 write!(f, "Permission denied: {}", path),
759/// #         }
760/// #     }
761/// # }
762///
763/// // Example usage:
764/// # use std::os::unix::net::UnixStream;
765/// # use zlink::test_utils::mock_socket::MockSocket;
766/// // Sending FDs: Pass indexes as regular parameters
767/// # let (stdin_pipe, _w1) = UnixStream::pair()?;
768/// # let (stdout_pipe, _w2) = UnixStream::pair()?;
769/// # let send_response = r#"{"parameters":{"pid":1234}}"#;
770/// # let send_socket = MockSocket::with_responses(&[send_response]);
771/// # let mut send_conn = zlink::Connection::new(send_socket);
772/// let fds = vec![stdin_pipe.into(), stdout_pipe.into()];
773/// // Parameters reference FD indexes: stdin_fd=0, stdout_fd=1
774/// let result = send_conn.spawn_process("/bin/cat".to_string(), 0, 1, fds).await?;
775/// let process_info = result?;
776/// assert_eq!(process_info.pid, 1234);
777///
778/// // Receiving FDs: Response contains indexes that reference the FD vector
779/// # let (file1, _w3) = UnixStream::pair()?;
780/// # let (file2, _w4) = UnixStream::pair()?;
781/// # let recv_response = r#"{
782/// #   "parameters": [
783/// #     {"path": "/etc/config.txt", "fd": 0},
784/// #     {"path": "/var/data.bin", "fd": 1}
785/// #   ]
786/// # }"#;
787/// # let recv_fds = vec![vec![file1.into(), file2.into()]];
788/// # let recv_socket = MockSocket::new(&[recv_response], recv_fds);
789/// # let mut recv_conn = zlink::Connection::new(recv_socket);
790/// let (result, received_fds) = recv_conn
791///     .open_files(vec!["/etc/config.txt".to_string(), "/var/data.bin".to_string()])
792///     .await?;
793/// let file_list = result?;
794/// assert_eq!(file_list.len(), 2);
795/// assert_eq!(received_fds.len(), 2);
796/// // Use the fd field to match file info with actual FDs
797/// for file_info in &file_list {
798///     let fd = &received_fds[file_info.fd as usize];
799///     println!("File {} has FD at index {}", file_info.path, file_info.fd);
800/// }
801/// # Ok::<(), Box<dyn std::error::Error>>(())
802/// # }).unwrap();
803/// ```
804///
805/// ## Parameter Renaming
806///
807/// Use `#[zlink(rename = "name")]` on parameters to customize their serialized names in the
808/// Varlink protocol. This is useful when the Rust parameter name doesn't match the expected
809/// Varlink parameter name.
810///
811/// ```rust
812/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
813/// # use zlink::proxy;
814/// # use serde::{Deserialize, Serialize};
815/// #[proxy("org.example.Users")]
816/// trait UsersProxy {
817///     async fn create_user(
818///         &mut self,
819///         #[zlink(rename = "user_name")] name: String,
820///         #[zlink(rename = "user_email")] email: String,
821///     ) -> zlink::Result<Result<UserId, UserError>>;
822/// }
823/// # #[derive(Debug, Serialize, Deserialize)]
824/// # struct UserId { id: u32 }
825/// # #[derive(Debug, Serialize, Deserialize)]
826/// # #[serde(tag = "error")]
827/// # enum UserError { InvalidInput }
828/// # Ok::<(), Box<dyn std::error::Error>>(())
829/// # }).unwrap();
830/// ```
831///
832/// # Generic Parameters
833///
834/// The proxy macro supports generic type parameters on individual methods. Note that generic
835/// parameters on the trait itself are not currently supported.
836///
837/// ```rust
838/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
839/// # use zlink::proxy;
840/// # use serde::{Deserialize, Serialize};
841/// # #[derive(Debug, Serialize, Deserialize)]
842/// # struct StoredValue<T> { data: T }
843/// # #[derive(Debug, Serialize, Deserialize)]
844/// # struct ProcessReply<'a> { result: &'a str }
845/// # #[derive(Debug, Serialize, Deserialize)]
846/// # #[serde(tag = "error")]
847/// # enum StorageError { NotFound }
848/// # impl std::fmt::Display for StorageError {
849/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
850/// #         write!(f, "Storage error")
851/// #     }
852/// # }
853/// # impl std::error::Error for StorageError {}
854/// #
855/// #[proxy("org.example.Storage")]
856/// trait StorageProxy {
857///     // Method-level generics with trait bounds
858///     async fn store<'a, T: Serialize + std::fmt::Debug>(
859///         &mut self,
860///         key: &'a str,
861///         value: T,
862///     ) -> zlink::Result<Result<(), StorageError>>;
863///
864///     // Generic methods with where clauses
865///     async fn process<T>(&mut self, data: T)
866///         -> zlink::Result<Result<ProcessReply<'_>, StorageError>>
867///     where
868///         T: Serialize + std::fmt::Debug;
869///
870///     // Methods can use generic type parameters in both input and output
871///     async fn store_and_return<'a, T>(&mut self, key: &'a str, value: T)
872///         -> zlink::Result<Result<StoredValue<T>, StorageError>>
873///     where
874///         T: Serialize + for<'de> Deserialize<'de> + std::fmt::Debug;
875/// }
876///
877/// // Example usage:
878/// # use zlink::test_utils::mock_socket::MockSocket;
879/// # let responses = vec![
880/// #     r#"{"parameters":null}"#, // store returns empty Ok
881/// # ];
882/// # let socket = MockSocket::with_responses(&responses);
883/// # let mut conn = zlink::Connection::new(socket);
884/// // Store a value with generic type
885/// let result = conn.store("my-key", 42i32).await?;
886/// assert!(result.is_ok());
887/// # Ok::<(), Box<dyn std::error::Error>>(())
888/// # }).unwrap();
889/// ```
890#[cfg(feature = "proxy")]
891#[proc_macro_attribute]
892pub fn proxy(
893    attr: proc_macro::TokenStream,
894    input: proc_macro::TokenStream,
895) -> proc_macro::TokenStream {
896    proxy::proxy(attr.into(), input.into()).into()
897}
898
899/// Implements `serde::{Serialize, Deserialize}` for service error enums.
900///
901/// This macro automatically generates both `Serialize` and `Deserialize` implementations for error
902/// types that are used in Varlink service replies.
903///
904/// The macro works in both `std` and `no_std` environments and requires the "error" field
905/// to appear before "parameters" field in JSON for efficient parsing.
906///
907/// # Supported Enum Variants
908///
909/// The macro supports:
910/// - **Unit variants**: Variants without any data
911/// - **Named field variants**: Variants with named fields
912///
913/// Tuple variants are **not** supported.
914///
915/// # Attributes
916///
917/// ## Enum-level attributes
918///
919/// - `interface` - This mandatory attribute specifies the Varlink interface name (e.g.,
920///   "org.varlink.service")
921///
922/// ## Field-level attributes
923///
924/// - `rename = "..."` - Specifies a custom name for the field in the JSON representation
925/// - `borrow` - Enables zero-copy deserialization for types like `Cow<'_, str>`
926///
927/// # Example
928///
929/// ```rust
930/// use std::borrow::Cow;
931/// use zlink::ReplyError;
932///
933/// #[derive(ReplyError)]
934/// #[zlink(interface = "com.example.MyService")]
935/// enum ServiceError<'a> {
936///     // Unit variant - no parameters
937///     NotFound,
938///     PermissionDenied,
939///
940///     // Named field variant - multiple parameters
941///     InvalidInput {
942///         field: String,
943///         reason: String,
944///     },
945///
946///     // Variant with zero-copy deserialization using borrow
947///     CustomError {
948///         #[zlink(borrow)]
949///         message: Cow<'a, str>,
950///     },
951///
952///     // Variant with renamed field
953///     Timeout {
954///         #[zlink(rename = "timeoutSeconds")]
955///         seconds: u32,
956///     },
957/// }
958///
959/// // The macro generates:
960/// // - `Serialize` impl that creates properly tagged enum format
961/// // - `Deserialize` impl that handles the tagged enum format efficiently
962/// ```
963///
964/// # Serialization Format
965///
966/// The generated serialization uses a tagged enum format:
967///
968/// ```json
969/// // Unit variant:
970/// {"error": "NotFound"}
971/// // or with empty parameters:
972/// {"error": "NotFound", "parameters": null}
973///
974/// // Variant with fields:
975/// {
976///   "error": "InvalidInput",
977///   "parameters": {
978///     "field": "username",
979///     "reason": "too short"
980///   }
981/// }
982/// ```
983#[proc_macro_derive(ReplyError, attributes(zlink))]
984pub fn derive_reply_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
985    reply_error::derive_reply_error(input)
986}