altium_format_derive/lib.rs
1// SPDX-License-Identifier: GPL-3.0-only
2// SPDX-FileCopyrightText: 2026 Alexander Kiselev <alex@akiselev.com>
3//
4//! Derive macros for Altium record types.
5//!
6//! This crate provides procedural macros for automatically generating
7//! serialization/deserialization code for Altium file format records.
8//!
9//! # Macros
10//!
11//! - `AltiumRecord` - Derive for record types (generates FromParams/ToParams or FromBinary/ToBinary)
12//! - `AltiumBase` - Derive for base types (generates HasXxxBase traits)
13//! - `AltiumEnum` - Derive for enum value mapping
14
15use proc_macro::TokenStream;
16use syn::{parse_macro_input, DeriveInput};
17
18mod attrs;
19mod base;
20mod enum_derive;
21mod record;
22
23/// Derive macro for Altium record types.
24///
25/// # Container Attributes
26///
27/// - `#[altium(record_id = N)]` - Schematic record type ID
28/// - `#[altium(object_id = Variant)]` - PCB object ID enum variant
29/// - `#[altium(format = "params"|"binary"|"both")]` - Serialization format
30///
31/// # Field Attributes
32///
33/// - `#[altium(flatten)]` - Flatten a base type's fields
34/// - `#[altium(param = "KEY")]` - Map field to parameter key
35/// - `#[altium(param = "KEY", frac = "KEY_FRAC")]` - Integer with fractional part
36/// - `#[altium(param = "KEY", default)]` - Use Default::default() if missing
37/// - `#[altium(param = "KEY", default = value)]` - Use specific default value
38/// - `#[altium(param = "KEY", optional)]` - Wrap in Option<T>
39/// - `#[altium(binary, ty = "i32le")]` - Binary field type
40/// - `#[altium(binary, coord_point)]` - Binary coordinate point
41/// - `#[altium(unknown)]` - Store unknown parameters (non-destructive editing)
42/// - `#[altium(unknown_binary)]` - Store unknown binary bytes
43/// - `#[altium(skip)]` - Skip field entirely
44///
45/// # Example
46///
47/// ```ignore
48/// #[derive(AltiumRecord)]
49/// #[altium(record_id = 2, format = "params")]
50/// pub struct SchPin {
51/// #[altium(flatten)]
52/// pub base: SchGraphicalBase,
53///
54/// #[altium(param = "ELECTRICAL", default)]
55/// pub electrical: PinElectricalType,
56///
57/// #[altium(param = "PINLENGTH", frac = "PINLENGTH_FRAC")]
58/// pub pin_length: Coord,
59///
60/// #[altium(unknown)]
61/// pub unknown_params: UnknownFields,
62/// }
63/// ```
64#[proc_macro_derive(AltiumRecord, attributes(altium))]
65pub fn derive_altium_record(input: TokenStream) -> TokenStream {
66 let input = parse_macro_input!(input as DeriveInput);
67 record::derive_record(input)
68 .unwrap_or_else(|e| e.to_compile_error())
69 .into()
70}
71
72/// Derive macro for Altium base types.
73///
74/// Generates `HasXxxBase` traits for composition-based inheritance.
75///
76/// # Attributes
77///
78/// - `#[altium(base_name = "Name")]` - Name for generated trait (default: struct name)
79/// - `#[altium(extends = "ParentBase")]` - Parent base type for trait inheritance
80///
81/// # Example
82///
83/// ```ignore
84/// #[derive(AltiumBase)]
85/// #[altium(base_name = "SchPrimitiveBase")]
86/// pub struct SchPrimitiveBase {
87/// #[altium(param = "OWNERINDEX", default)]
88/// pub owner_index: i32,
89/// }
90///
91/// #[derive(AltiumBase)]
92/// #[altium(base_name = "SchGraphicalBase", extends = "SchPrimitiveBase")]
93/// pub struct SchGraphicalBase {
94/// #[altium(flatten)]
95/// pub base: SchPrimitiveBase,
96///
97/// #[altium(param = "LOCATION.X", frac = "LOCATION.X_FRAC")]
98/// pub location_x: i32,
99/// }
100/// ```
101#[proc_macro_derive(AltiumBase, attributes(altium))]
102pub fn derive_altium_base(input: TokenStream) -> TokenStream {
103 let input = parse_macro_input!(input as DeriveInput);
104 base::derive_base(input)
105 .unwrap_or_else(|e| e.to_compile_error())
106 .into()
107}
108
109/// Derive macro for Altium enums with integer mapping.
110///
111/// # Attributes
112///
113/// - `#[altium(repr = "i32"|"u8"|...)]` - Integer representation type
114/// - `#[altium(value = N)]` - Map variant to specific integer value
115/// - `#[altium(default)]` - Mark variant as default for unknown values
116///
117/// # Example
118///
119/// ```ignore
120/// #[derive(AltiumEnum)]
121/// #[altium(repr = "i32")]
122/// pub enum PinElectricalType {
123/// #[altium(value = 0)]
124/// Input,
125/// #[altium(value = 1)]
126/// InputOutput,
127/// #[altium(value = 2)]
128/// Output,
129/// #[altium(default)]
130/// Passive = 4,
131/// }
132/// ```
133#[proc_macro_derive(AltiumEnum, attributes(altium))]
134pub fn derive_altium_enum(input: TokenStream) -> TokenStream {
135 let input = parse_macro_input!(input as DeriveInput);
136 enum_derive::derive_enum(input)
137 .unwrap_or_else(|e| e.to_compile_error())
138 .into()
139}