automorph_derive/lib.rs
1#![forbid(unsafe_code)]
2
3//! Derive macro for the Automorph trait.
4//!
5//! This crate provides `#[derive(Automorph)]` to automatically implement
6//! bidirectional synchronization with Automerge documents.
7//!
8//! ## Supported Structures
9//!
10//! - Named structs: `struct Foo { bar: i32 }`
11//! - Tuple structs (newtype): `struct Foo(Bar)`
12//! - Unit structs: `struct Foo`
13//! - Enums with all variant types
14//!
15//! ## Container Attributes
16//!
17//! - `#[automorph(rename_all = "...")]` - Rename all fields (camelCase, snake_case, etc.)
18//! - `#[automorph(tag = "...")]` - Internal tagging for enums
19//! - `#[automorph(tag = "...", content = "...")]` - Adjacent tagging for enums
20//! - `#[automorph(untagged)]` - Untagged enum representation
21//!
22//! ## Field Attributes
23//!
24//! - `#[automorph(rename = "...")]` - Rename this field
25//! - `#[automorph(skip)]` - Skip this field entirely
26//! - `#[automorph(default)]` - Use Default if field is missing
27
28mod attribute_parsing;
29mod case_conversion;
30mod enum_derive;
31mod struct_derive;
32
33use proc_macro::TokenStream;
34use syn::{Data, DeriveInput, parse_macro_input};
35
36use crate::attribute_parsing::parse_container_attrs;
37
38/// Derive the `Automorph` trait for a struct or enum.
39///
40/// # Example
41///
42/// ```ignore
43/// use automorph::Automorph;
44///
45/// #[derive(Automorph, Default, Clone)]
46/// struct Person {
47/// name: String,
48/// age: u64,
49/// }
50/// ```
51///
52/// This generates an implementation that stores the struct as an Automerge Map
53/// with each field as a key.
54#[proc_macro_derive(Automorph, attributes(automorph))]
55pub fn derive_automorph(input: TokenStream) -> TokenStream {
56 let input = parse_macro_input!(input as DeriveInput);
57
58 // Check for transparent attribute
59 let container_attrs = parse_container_attrs(&input.attrs);
60
61 match &input.data {
62 Data::Struct(data) => {
63 // Handle transparent newtype structs
64 if container_attrs.transparent {
65 struct_derive::derive_transparent_struct(&input, data)
66 } else {
67 struct_derive::derive_struct(&input, data)
68 }
69 }
70 Data::Enum(data) => enum_derive::derive_enum(&input, data),
71 Data::Union(_) => {
72 syn::Error::new(input.ident.span(), "Automorph cannot be derived for unions")
73 .to_compile_error()
74 .into()
75 }
76 }
77}