+ use proc_macro::TokenStream;
+ use quote::{format_ident, quote};
+ use syn::{parse_macro_input, DeriveInput};
+
+ #[proc_macro_derive(Output)]
+ pub fn output(input: TokenStream) -> TokenStream {
+ let DeriveInput { ident, data, .. } = parse_macro_input!(input);
+
+ match data {
+ syn::Data::Struct(struct_data) => {
+ let builder_ident = format_ident!("{ident}Builder");
+
+ // Get all field identifiers in the struct
+ // Each field is assumed to have a unique identifier (Rust should enforce this?)
+ let field_idents = struct_data
+ .fields
+ .iter()
+ .map(|field| field.ident.clone().unwrap()); // TODO: handle tuple structs
+
+ let types = struct_data.fields.iter().map(|field| field.ty.clone());
+ let ident_type_mappings = field_idents.clone().zip(types.clone());
+
+ // In the builder struct definition, map each field from `field: T` to `field: Option<T>`
+ let optional_mappings = ident_type_mappings
+ .clone()
+ .map(|(field_ident, ty)| quote! { #field_ident: Option<#ty> });
+
+ // Functions to handle incoming data
+ // In the future these functions will include logic for immediate output to stdout (human output),
+ // just lazily setting (machine output), or something else (maybe test handler?)
+ // TODO: investigate setting collections, e.g. have the setter extend rather than set the collection so that
+ // it can be immmediately output rather than to buffer all items in collection
+ let setter_functions = ident_type_mappings.clone().map(|(field_ident, ty)| {
+ quote! { fn #field_ident(&mut self, value: #ty) -> &mut Self {
+ assert!(self.#field_ident.is_none());
+ self.#field_ident = Some(value);
+
+ self
+ }}
+ });
+
+ // Simple getter functions to inspect the value set in the builder
+ let getter_functions = field_idents
+ .clone()
+ .map(|field_ident| format_ident!("get_{field_ident}"))
+ .zip(field_idents.clone())
+ .zip(types.clone())
+ .map(|((function_ident, field_ident), ty)| {
+ quote! { fn #function_ident(&self) -> &Option<#ty> {
+ &self.#field_ident
+ }}
+ });
+
+ // Construct the builder implementation and return the token stream
+ quote! {
+ struct #builder_ident {
+ #(#optional_mappings),*
+ }
+
+ impl #builder_ident {
+ const fn new() -> Self {
+ Self {
+ #(#field_idents: None),*
+ }
+ }
+
+ #(#setter_functions)*
+ #(#getter_functions)*
+ }
+
+ impl #ident {
+ pub const fn new() -> #builder_ident {
+ #builder_ident::new()
+ }
+ }
+
+ }
+ .into()
+ }
+ _ => todo!("Handle deriving non-structs"),
+ }
+ }