zlink_macros/
lib.rs

1#![deny(
2    missing_debug_implementations,
3    nonstandard_style,
4    rust_2018_idioms,
5    missing_docs
6)]
7#![warn(unreachable_pub)]
8#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
9
10mod utils;
11
12#[cfg(feature = "introspection")]
13mod introspect;
14
15mod reply_error;
16
17#[cfg(feature = "proxy")]
18mod proxy;
19
20/// Derives `Type` for structs and enums, generating appropriate `Type::Object` or `Type::Enum`
21/// representation.
22///
23/// **Requires the `introspection` feature to be enabled.**
24///
25/// ## Structs
26///
27/// For structs, this macro supports named fields and unit structs. It will generate a
28/// `Type` implementation that creates a `Type::Object` containing all the fields with their
29/// names and types. Tuple structs are not supported as Varlink does not support unnamed fields.
30///
31/// ## Enums
32///
33/// For enums, this macro only supports unit variants (variants without associated data). It will
34/// generate a `Type` implementation that creates a `Type::Enum` containing all the variant
35/// names.
36///
37/// # Supported Attributes
38///
39/// The following attributes can be used to customize the behavior of this derive macro:
40///
41/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
42///   `::zlink`.
43///
44/// # Limitations
45///
46/// The following types are **not** supported by this macro:
47///
48/// - **Tuple structs**: Varlink does not support unnamed fields
49/// - **Enums with data**: Only unit enums (variants without associated data) are supported
50/// - **Unions**: Not supported by Varlink
51///
52/// ```rust,compile_fail
53/// # use zlink::introspect::Type;
54/// #[derive(Type)]  // This will fail to compile
55/// struct Point(f32, f32, f32);
56/// ```
57///
58/// ```rust,compile_fail
59/// # use zlink::introspect::Type;
60/// #[derive(Type)]  // This will fail to compile
61/// enum Status {
62///     Active(String),  // Variants with data are not supported
63///     Inactive,
64/// }
65/// ```
66///
67/// # Examples
68///
69/// ## Named Structs
70///
71/// ```rust
72/// use zlink::introspect::Type;
73/// use zlink::idl;
74///
75/// #[derive(Type)]
76/// struct Person {
77///     name: String,
78///     age: i32,
79///     active: bool,
80/// }
81///
82/// // Access the generated type information
83/// match Person::TYPE {
84///     idl::Type::Object(fields) => {
85///         let field_vec: Vec<_> = fields.iter().collect();
86///         assert_eq!(field_vec.len(), 3);
87///
88///         assert_eq!(field_vec[0].name(), "name");
89///         assert_eq!(field_vec[0].ty(), &idl::Type::String);
90///
91///         assert_eq!(field_vec[1].name(), "age");
92///         assert_eq!(field_vec[1].ty(), &idl::Type::Int);
93///
94///         assert_eq!(field_vec[2].name(), "active");
95///         assert_eq!(field_vec[2].ty(), &idl::Type::Bool);
96///     }
97///     _ => panic!("Expected struct type"),
98/// }
99/// ```
100///
101/// ## Unit Structs
102///
103/// ```rust
104/// # use zlink::introspect::Type;
105/// # use zlink::idl;
106/// #[derive(Type)]
107/// struct Unit;
108///
109/// // Unit structs generate empty field lists
110/// match Unit::TYPE {
111///     idl::Type::Object(fields) => {
112///         assert_eq!(fields.len(), 0);
113///     }
114///     _ => panic!("Expected struct type"),
115/// }
116/// ```
117///
118/// ## Complex Types
119///
120/// ```rust
121/// # use zlink::introspect::Type;
122/// # use zlink::idl;
123/// #[derive(Type)]
124/// struct Complex {
125///     id: u64,
126///     description: Option<String>,
127///     tags: Vec<String>,
128/// }
129///
130/// // The macro handles nested types like Option<T> and Vec<T>
131/// match Complex::TYPE {
132///     idl::Type::Object(fields) => {
133///         let field_vec: Vec<_> = fields.iter().collect();
134///
135///         // Optional field becomes Type::Optional
136///         match field_vec[1].ty() {
137///             idl::Type::Optional(inner) => assert_eq!(inner.inner(), &idl::Type::String),
138///             _ => panic!("Expected optional type"),
139///         }
140///
141///         // Vec field becomes Type::Array
142///         match field_vec[2].ty() {
143///             idl::Type::Array(inner) => assert_eq!(inner.inner(), &idl::Type::String),
144///             _ => panic!("Expected array type"),
145///         }
146///     }
147///     _ => panic!("Expected struct type"),
148/// }
149/// ```
150///
151/// ## Unit Enums
152///
153/// ```rust
154/// # use zlink::introspect::Type;
155/// # use zlink::idl;
156/// #[derive(Type)]
157/// enum Status {
158///     Active,
159///     Inactive,
160///     Pending,
161/// }
162///
163/// // Unit enums generate variant lists
164/// match Status::TYPE {
165///     idl::Type::Enum(variants) => {
166///         let variant_vec: Vec<_> = variants.iter().collect();
167///         assert_eq!(variant_vec.len(), 3);
168///         assert_eq!(variant_vec[0].name(), "Active");
169///         assert_eq!(variant_vec[1].name(), "Inactive");
170///         assert_eq!(variant_vec[2].name(), "Pending");
171///     }
172///     _ => panic!("Expected enum type"),
173/// }
174/// ```
175#[cfg(feature = "introspection")]
176#[proc_macro_derive(IntrospectType, attributes(zlink))]
177pub fn derive_introspect_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
178    introspect::r#type::derive_type(input)
179}
180
181/// Derives `Type` for structs and enums, generating named custom type definitions.
182///
183/// **Requires the `introspection` feature to be enabled.**
184///
185/// This macro generates implementations of the `CustomType` trait, which provides named
186/// custom type definitions suitable for IDL generation. It also generates a `Type` implementation
187/// and therefore is mutually exclusive to `zlink::introspect::Type` derive macro.
188///
189/// ## Structs
190///
191/// For structs, this macro generates a `custom::Type::Object` containing the struct name and
192/// all fields with their names and types.
193///
194/// ## Enums
195///
196/// For enums, this macro only supports unit variants and generates a `custom::Type::Enum`
197/// containing the enum name and all variant names.
198///
199/// # Supported Attributes
200///
201/// The following attributes can be used to customize the behavior of this derive macro:
202///
203/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
204///   `::zlink`.
205///
206/// # Examples
207///
208/// ## Named Structs
209///
210/// ```rust
211/// use zlink::introspect::{CustomType, Type};
212/// use zlink::idl;
213///
214/// #[derive(CustomType)]
215/// struct Point {
216///     x: f64,
217///     y: f64,
218/// }
219///
220/// // Access the generated custom type information
221/// match Point::CUSTOM_TYPE {
222///     idl::CustomType::Object(obj) => {
223///         assert_eq!(obj.name(), "Point");
224///         let fields: Vec<_> = obj.fields().collect();
225///         assert_eq!(fields.len(), 2);
226///         assert_eq!(fields[0].name(), "x");
227///         assert_eq!(fields[1].name(), "y");
228///     }
229///     _ => panic!("Expected custom object type"),
230/// }
231///
232/// match Point::TYPE {
233///     idl::Type::Custom(name) => {
234///         assert_eq!(*name, "Point");
235///     }
236///     _ => panic!("Expected custom type"),
237/// }
238/// ```
239///
240/// ## Unit Enums
241///
242/// ```rust
243/// # use zlink::introspect::{CustomType, Type};
244/// # use zlink::idl;
245/// #[derive(CustomType)]
246/// enum Status {
247///     Active,
248///     Inactive,
249///     Pending,
250/// }
251///
252/// // Access the generated custom enum type information
253/// match Status::CUSTOM_TYPE {
254///     idl::CustomType::Enum(enm) => {
255///         assert_eq!(enm.name(), "Status");
256///         let variants: Vec<_> = enm.variants().collect();
257///         assert_eq!(variants.len(), 3);
258///         assert_eq!(variants[0].name(), "Active");
259///         assert_eq!(variants[1].name(), "Inactive");
260///         assert_eq!(variants[2].name(), "Pending");
261///     }
262///     _ => panic!("Expected custom enum type"),
263/// }
264///
265/// match Status::TYPE {
266///     idl::Type::Custom(name) => {
267///         assert_eq!(*name, "Status");
268///     }
269///     _ => panic!("Expected custom type"),
270/// }
271/// ```
272#[cfg(feature = "introspection")]
273#[proc_macro_derive(IntrospectCustomType, attributes(zlink))]
274pub fn derive_introspect_custom_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
275    introspect::custom_type::derive_custom_type(input)
276}
277
278/// Derives `ReplyError` for enums, generating error definitions for Varlink service errors.
279///
280/// **Requires the `introspection` feature to be enabled.**
281///
282/// This macro generates implementations of the `ReplyError` trait, which provides a list of
283/// error variants that can be returned by a Varlink service method. It supports unit variants,
284/// variants with named fields, and single-field tuple variants (where the field type implements
285/// `Type` and has a `Type::Object`).
286///
287/// # Supported Attributes
288///
289/// The following attributes can be used to customize the behavior of this derive macro:
290///
291/// * `#[zlink(crate = "path")]` - Specifies the crate path to use for zlink types. Defaults to
292///   `::zlink`.
293///
294/// # Example
295///
296/// ```rust
297/// use zlink::introspect::ReplyError;
298///
299/// #[derive(ReplyError)]
300/// enum ServiceError {
301///     // Unit variant - no parameters
302///     NotFound,
303///
304///     // Named field variant - multiple parameters
305///     InvalidQuery {
306///         message: String,
307///         line: u32,
308///     },
309///
310///     // Single tuple variant - uses fields from the wrapped type
311///     ValidationFailed(ValidationDetails),
312/// }
313///
314/// // Example struct for tuple variant
315/// #[derive(zlink::introspect::Type)]
316/// struct ValidationDetails {
317///     field_name: String,
318///     expected: String,
319/// }
320///
321/// // Access the generated error variants
322/// assert_eq!(ServiceError::VARIANTS.len(), 3);
323/// assert_eq!(ServiceError::VARIANTS[0].name(), "NotFound");
324/// assert!(ServiceError::VARIANTS[0].has_no_fields());
325///
326/// assert_eq!(ServiceError::VARIANTS[1].name(), "InvalidQuery");
327/// assert!(!ServiceError::VARIANTS[1].has_no_fields());
328/// ```
329#[cfg(feature = "introspection")]
330#[proc_macro_derive(IntrospectReplyError, attributes(zlink))]
331pub fn derive_introspect_reply_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
332    introspect::reply_error::derive_reply_error(input)
333}
334
335/// Creates a client-side proxy for calling Varlink methods on a connection.
336///
337/// **Requires the `proxy` feature to be enabled.**
338///
339/// This attribute macro generates an implementation of the provided trait for `Connection<S>`,
340/// automatically handling the serialization of method calls and deserialization of responses.
341/// Each proxy trait targets a single Varlink interface.
342///
343/// The macro also generates a chain extension trait that allows you to chain multiple method
344/// calls together for efficient batching across multiple interfaces.
345///
346/// # Supported Attributes
347///
348/// The following attributes can be used to customize the behavior of this macro:
349///
350/// * `interface` (required) - The Varlink interface name (e.g., `"org.varlink.service"`).
351/// * `crate` - Specifies the crate path to use for zlink types. Defaults to `::zlink`.
352/// * `chain_name` - Custom name for the generated chain extension trait. Defaults to
353///   `{TraitName}Chain`.
354///
355/// # Example
356///
357/// ```rust
358/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
359/// use zlink::proxy;
360/// use serde::{Deserialize, Serialize};
361/// use serde_prefix_all::prefix_all;
362/// use futures_util::stream::Stream;
363///
364/// #[proxy("org.example.MyService")]
365/// trait MyServiceProxy {
366///     async fn get_status(&mut self) -> zlink::Result<Result<Status<'_>, MyError<'_>>>;
367///     async fn set_value(
368///         &mut self,
369///         key: &str,
370///         value: i32,
371///     ) -> zlink::Result<Result<(), MyError<'_>>>;
372///     // This will call the `io.systemd.Machine.List` method when `list_machines()` is invoked.
373///     #[zlink(rename = "ListMachines")]
374///     async fn list_machines(&mut self) -> zlink::Result<Result<Vec<Machine<'_>>, MyError<'_>>>;
375///     // Streaming version of get_status - calls the same method but returns a stream
376///     #[zlink(rename = "GetStatus", more)]
377///     async fn stream_status(
378///         &mut self,
379///     ) -> zlink::Result<
380///         impl Stream<Item = zlink::Result<Result<Status<'_>, MyError<'_>>>>,
381///     >;
382/// }
383///
384/// // The macro generates:
385/// // impl<S: Socket> MyServiceProxy for Connection<S> { ... }
386///
387/// #[derive(Debug, Serialize, Deserialize)]
388/// struct Status<'m> {
389///     active: bool,
390///     message: &'m str,
391/// }
392///
393/// #[derive(Debug, Serialize, Deserialize)]
394/// struct Machine<'m> { name: &'m str }
395///
396/// #[prefix_all("org.example.MyService.")]
397/// #[derive(Debug, Serialize, Deserialize)]
398/// #[serde(tag = "error", content = "parameters")]
399/// enum MyError<'a> {
400///     NotFound,
401///     InvalidRequest,
402///     // Parameters must be named.
403///     CodedError { code: u32, message: &'a str },
404/// }
405///
406/// // Example usage:
407/// # use zlink::test_utils::mock_socket::MockSocket;
408/// # let responses = vec![
409/// #     r#"{"parameters":{"active":true,"message":"System running"}}"#,
410/// # ];
411/// # let socket = MockSocket::new(&responses);
412/// # let mut conn = zlink::Connection::new(socket);
413/// let result = conn.get_status().await?.unwrap();
414/// assert_eq!(result.active, true);
415/// assert_eq!(result.message, "System running");
416/// # Ok::<(), Box<dyn std::error::Error>>(())
417/// # }).unwrap();
418/// ```
419///
420/// # Chaining Method Calls
421///
422/// The proxy macro generates chain extension traits that allow you to batch multiple method calls
423/// together. This is useful for reducing round trips and efficiently calling methods across
424/// multiple interfaces. Each method gets a `chain_` prefixed variant that starts a chain.
425///
426/// ## Example: Chaining Method Calls
427///
428/// ```rust
429/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
430/// # use zlink::proxy;
431/// # use serde::{Deserialize, Serialize};
432/// # use futures_util::{pin_mut, TryStreamExt};
433/// #
434/// # #[derive(Debug, Serialize, Deserialize)]
435/// # struct User<'a> { id: u64, name: &'a str }
436/// # #[derive(Debug, Serialize, Deserialize)]
437/// # struct Post<'a> { id: u64, user_id: u64, content: &'a str }
438/// # #[derive(Debug, Serialize, Deserialize)]
439/// # #[serde(untagged)]
440/// # enum BlogReply<'a> {
441/// #     #[serde(borrow)]
442/// #     User(User<'a>),
443/// #     #[serde(borrow)]
444/// #     Post(Post<'a>),
445/// #     #[serde(borrow)]
446/// #     Posts(Vec<Post<'a>>)
447/// # }
448/// # #[derive(Debug, Serialize, Deserialize)]
449/// # #[serde(tag = "error")]
450/// # enum BlogError { NotFound, InvalidInput }
451/// # impl std::fmt::Display for BlogError {
452/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
453/// #         match self {
454/// #             Self::NotFound => write!(f, "Not found"),
455/// #             Self::InvalidInput => write!(f, "Invalid input")
456/// #         }
457/// #     }
458/// # }
459/// # impl std::error::Error for BlogError {}
460/// #
461/// // Define proxies for two different services
462/// #[proxy("org.example.blog.Users")]
463/// trait UsersProxy {
464///     async fn get_user(&mut self, id: u64) -> zlink::Result<Result<BlogReply<'_>, BlogError>>;
465///     async fn create_user(&mut self, name: &str)
466///         -> zlink::Result<Result<BlogReply<'_>, BlogError>>;
467/// }
468///
469/// #[proxy("org.example.blog.Posts")]
470/// trait PostsProxy {
471///     async fn get_posts_by_user(&mut self, user_id: u64)
472///         -> zlink::Result<Result<BlogReply<'_>, BlogError>>;
473///     async fn create_post(&mut self, user_id: u64, content: &str)
474///         -> zlink::Result<Result<BlogReply<'_>, BlogError>>;
475/// }
476///
477/// # use zlink::test_utils::mock_socket::MockSocket;
478/// # let responses = vec![
479/// #     r#"{"parameters":{"id":1,"name":"Alice"}}"#,
480/// #     r#"{"parameters":{"id":1,"user_id":1,"content":"My first post!"}}"#,
481/// #     r#"{"parameters":[{"id":1,"user_id":1,"content":"My first post!"}]}"#,
482/// #     r#"{"parameters":{"id":1,"name":"Alice"}}"#,
483/// # ];
484/// # let socket = MockSocket::new(&responses);
485/// # let mut conn = zlink::Connection::new(socket);
486/// // Chain calls across both interfaces in a single batch
487/// let chain = conn
488///     .chain_create_user::<BlogReply<'_>, BlogError>("Alice")? // Start with Users interface
489///     .create_post(1, "My first post!")?                       // Chain Posts interface
490///     .get_posts_by_user(1)?                                   // Get all posts
491///     .get_user(1)?;                                           // Get user details
492///
493/// // Send all calls in a single batch
494/// let replies = chain.send().await?;
495/// pin_mut!(replies);
496///
497/// // Process replies in order
498/// let mut reply_count = 0;
499/// while let Some(reply) = replies.try_next().await? {
500///     let reply = reply?;
501///     reply_count += 1;
502///     match reply.parameters() {
503///         Some(BlogReply::User(user)) => assert_eq!(user.name, "Alice"),
504///         Some(BlogReply::Post(post)) => assert_eq!(post.content, "My first post!"),
505///         Some(BlogReply::Posts(posts)) => assert_eq!(posts.len(), 1),
506///         None => {} // set_value returns empty response
507///     }
508/// }
509/// assert_eq!(reply_count, 4); // We made 4 calls
510/// # Ok::<(), Box<dyn std::error::Error>>(())
511/// # }).unwrap();
512/// ```
513///
514/// ## Combining with Standard Varlink Service
515///
516/// When the `idl-parse` feature is enabled, you can also chain calls between your custom
517/// interfaces and the standard Varlink service interface for introspection:
518///
519/// ```rust
520/// # #[cfg(feature = "idl-parse")] {
521/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
522/// # use zlink::{proxy, varlink_service};
523/// # use serde::{Deserialize, Serialize};
524/// #
525/// # #[derive(Debug, Serialize, Deserialize)]
526/// # struct Status { active: bool, message: String }
527/// # #[derive(Debug, Serialize, Deserialize)]
528/// # #[serde(tag = "error")]
529/// # enum MyError { NotFound, InvalidRequest }
530/// #
531/// #[proxy("com.example.MyService")]
532/// trait MyServiceProxy {
533///     async fn get_status(&mut self) -> zlink::Result<Result<Status, MyError>>;
534/// }
535///
536/// // Combined types for cross-interface chaining
537/// #[derive(Debug, Deserialize)]
538/// #[serde(untagged)]
539/// enum CombinedReply<'a> {
540///     #[serde(borrow)]
541///     VarlinkService(varlink_service::Reply<'a>),
542///     MyService(Status),
543/// }
544///
545/// #[derive(Debug, Deserialize)]
546/// #[serde(untagged)]
547/// enum CombinedError {
548///     VarlinkService(varlink_service::Error),
549///     MyService(MyError),
550/// }
551///
552/// // Example usage:
553/// # use zlink::test_utils::mock_socket::MockSocket;
554/// # let responses = vec![
555/// #     concat!(
556/// #         r#"{"parameters":{"vendor":"Test","product":"Example","version":"1.0","#,
557/// #         r#""url":"https://example.com","interfaces":["com.example.MyService","#,
558/// #         r#""org.varlink.service"]}}"#
559/// #     ),
560/// #     r#"{"parameters":{"active":true,"message":"Running"}}"#,
561/// #     r#"{"parameters":{"description":"interface com.example.MyService\n..."}}"#,
562/// # ];
563/// # let socket = MockSocket::new(&responses);
564/// # let mut conn = zlink::Connection::new(socket);
565/// use varlink_service::Proxy;
566/// use zlink::varlink_service::Chain;
567///
568/// // Get service info and custom status in one batch
569/// let chain = conn
570///     .chain_get_info::<CombinedReply<'_>, CombinedError>()? // Varlink service interface
571///     .get_status()?                                         // MyService interface
572///     .get_interface_description("com.example.MyService")?;  // Back to Varlink service
573///
574/// let replies = chain.send().await?;
575/// # Ok::<(), Box<dyn std::error::Error>>(())
576/// # }).unwrap();
577/// # }
578/// ```
579///
580/// ## Chain Extension Traits
581///
582/// For each proxy trait, the macro generates a corresponding chain extension trait. For example,
583/// `FtlProxy` gets `FtlProxyChain`. This trait is automatically implemented for `Chain` types,
584/// allowing seamless method chaining across interfaces.
585///
586/// # Method Requirements
587///
588/// Proxy methods must:
589/// - Take `&mut self` as the first parameter
590/// - Can be either `async fn` or return `impl Future`
591/// - Return `zlink::Result<Result<ReplyType, ErrorType>>` (outer Result for connection errors,
592///   inner for method errors)
593/// - The arguments can be any type that implement `serde::Serialize`
594/// - The reply type (`Ok` case of the inner `Result`) must be a type that implements
595///   `serde::Deserialize` and deserializes itself from a JSON object. Typically you'd just use a
596///   struct that derives `serde::Deserialize`.
597/// - The reply error type (`Err` case of the inner `Result`) must be a type `serde::Deserialize`
598///   that deserializes itself from a JSON object with two fields:
599///   - `error`: a string containing the fully qualified error name
600///   - `parameters`: an optional object containing all the fields of the error
601///
602/// # Method Names
603///
604/// By default, method names are converted from snake_case to PascalCase for the Varlink call.
605/// To specify a different Varlink method name, use the `#[zlink(rename = "...")]` attribute. See
606/// `list_machines` in the example above.
607///
608/// # Streaming Methods
609///
610/// For methods that support streaming (the 'more' flag), use the `#[zlink(more)]` attribute.
611/// Streaming methods must return `Result<impl Stream<Item = Result<Result<ReplyType,
612/// ErrorType>>>>`. The proxy will automatically set the 'more' flag on the call and return a
613/// stream of replies.
614///
615/// # Generic Parameters
616///
617/// The proxy macro supports generic type parameters on individual methods. Note that generic
618/// parameters on the trait itself are not currently supported.
619///
620/// ```rust
621/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
622/// # use zlink::proxy;
623/// # use serde::{Deserialize, Serialize};
624/// # #[derive(Debug, Serialize, Deserialize)]
625/// # struct StoredValue<T> { data: T }
626/// # #[derive(Debug, Serialize, Deserialize)]
627/// # struct ProcessReply<'a> { result: &'a str }
628/// # #[derive(Debug, Serialize, Deserialize)]
629/// # #[serde(tag = "error")]
630/// # enum StorageError { NotFound }
631/// # impl std::fmt::Display for StorageError {
632/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
633/// #         write!(f, "Storage error")
634/// #     }
635/// # }
636/// # impl std::error::Error for StorageError {}
637/// #
638/// #[proxy("org.example.Storage")]
639/// trait StorageProxy {
640///     // Method-level generics with trait bounds
641///     async fn store<'a, T: Serialize + std::fmt::Debug>(
642///         &mut self,
643///         key: &'a str,
644///         value: T,
645///     ) -> zlink::Result<Result<(), StorageError>>;
646///
647///     // Generic methods with where clauses
648///     async fn process<T>(&mut self, data: T)
649///         -> zlink::Result<Result<ProcessReply<'_>, StorageError>>
650///     where
651///         T: Serialize + std::fmt::Debug;
652///
653///     // Methods can use generic type parameters in both input and output
654///     async fn store_and_return<'a, T>(&mut self, key: &'a str, value: T)
655///         -> zlink::Result<Result<StoredValue<T>, StorageError>>
656///     where
657///         T: Serialize + for<'de> Deserialize<'de> + std::fmt::Debug;
658/// }
659///
660/// // Example usage:
661/// # use zlink::test_utils::mock_socket::MockSocket;
662/// # let responses = vec![
663/// #     r#"{"parameters":null}"#, // store returns empty Ok
664/// # ];
665/// # let socket = MockSocket::new(&responses);
666/// # let mut conn = zlink::Connection::new(socket);
667/// // Store a value with generic type
668/// let result = conn.store("my-key", 42i32).await?;
669/// assert!(result.is_ok());
670/// # Ok::<(), Box<dyn std::error::Error>>(())
671/// # }).unwrap();
672/// ```
673#[cfg(feature = "proxy")]
674#[proc_macro_attribute]
675pub fn proxy(
676    attr: proc_macro::TokenStream,
677    input: proc_macro::TokenStream,
678) -> proc_macro::TokenStream {
679    proxy::proxy(attr.into(), input.into()).into()
680}
681
682/// Implements `serde::{Serialize, Deserialize}` for service error enums.
683///
684/// This macro automatically generates both `Serialize` and `Deserialize` implementations for error
685/// types that are used in Varlink service replies.
686///
687/// The macro works in both `std` and `no_std` environments and requires the "error" field
688/// to appear before "parameters" field in JSON for efficient parsing.
689///
690/// # Supported Enum Variants
691///
692/// The macro supports:
693/// - **Unit variants**: Variants without any data
694/// - **Named field variants**: Variants with named fields
695///
696/// Tuple variants are **not** supported.
697///
698/// # Attributes
699///
700/// - `interface` - This mandatory attribute specifies the Varlink interface name (e.g.,
701///   "org.varlink.service")
702///
703/// # Example
704///
705/// ```rust
706/// use zlink::ReplyError;
707///
708/// #[derive(ReplyError)]
709/// #[zlink(interface = "com.example.MyService")]
710/// enum ServiceError {
711///     // Unit variant - no parameters
712///     NotFound,
713///     PermissionDenied,
714///
715///     // Named field variant - multiple parameters
716///     InvalidInput {
717///         field: String,
718///         reason: String,
719///     },
720///
721///     // Another variant with a single field
722///     Timeout {
723///         seconds: u32,
724///     },
725/// }
726///
727/// // The macro generates:
728/// // - `Serialize` impl that creates properly tagged enum format
729/// // - `Deserialize` impl that handles the tagged enum format efficiently
730/// ```
731///
732/// # Serialization Format
733///
734/// The generated serialization uses a tagged enum format:
735///
736/// ```json
737/// // Unit variant:
738/// {"error": "NotFound"}
739/// // or with empty parameters:
740/// {"error": "NotFound", "parameters": null}
741///
742/// // Variant with fields:
743/// {
744///   "error": "InvalidInput",
745///   "parameters": {
746///     "field": "username",
747///     "reason": "too short"
748///   }
749/// }
750/// ```
751#[proc_macro_derive(ReplyError, attributes(zlink))]
752pub fn derive_reply_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
753    reply_error::derive_reply_error(input)
754}