From 76fd9207e5a2c16de1b15f35e53f87118b9bcfb6 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Fri, 19 May 2023 15:36:15 -0400 Subject: [PATCH] feat: derive macro for loading a cosmic-config --- Cargo.toml | 1 + cosmic-config-derive/Cargo.toml | 12 ++++++ cosmic-config-derive/src/lib.rs | 68 +++++++++++++++++++++++++++++++++ cosmic-config/Cargo.toml | 10 ++++- cosmic-config/src/lib.rs | 9 +++++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 cosmic-config-derive/Cargo.toml create mode 100644 cosmic-config-derive/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index dbbb4611..0879d0f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ optional = true [workspace] members = [ "cosmic-config", + "cosmic-config-derive", "examples/*", ] exclude = [ diff --git a/cosmic-config-derive/Cargo.toml b/cosmic-config-derive/Cargo.toml new file mode 100644 index 00000000..44f960ec --- /dev/null +++ b/cosmic-config-derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cosmic-config-derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +proc-macro = true + +[dependencies] +syn = "1.0" +quote = "1.0" diff --git a/cosmic-config-derive/src/lib.rs b/cosmic-config-derive/src/lib.rs new file mode 100644 index 00000000..9f6fe37e --- /dev/null +++ b/cosmic-config-derive/src/lib.rs @@ -0,0 +1,68 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn; + +#[proc_macro_derive(CosmicConfigEntry)] +pub fn cosmic_config_entry_derive(input: TokenStream) -> TokenStream { + // Construct a representation of Rust code as a syntax tree + // that we can manipulate + let ast = syn::parse(input).unwrap(); + + // Build the trait implementation + impl_cosmic_config_entry_macro(&ast) +} + +fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream { + let name = &ast.ident; + + // Get the fields of the struct + let fields = match ast.data { + syn::Data::Struct(ref data_struct) => match data_struct.fields { + syn::Fields::Named(ref fields) => &fields.named, + _ => unimplemented!("Only named fields are supported"), + }, + _ => unimplemented!("Only structs are supported"), + }; + + let write_each_config_field = fields.iter().map(|field| { + let field_name = &field.ident; + quote! { + config.set(stringify!(#field_name), &self.#field_name)?; + } + }); + + let get_each_config_field = fields.iter().map(|field| { + let field_name = &field.ident; + let field_type = &field.ty; + quote! { + match config.get::<#field_type>(stringify!(#field_name)) { + Ok(#field_name) => default.#field_name = #field_name, + Err(e) => errors.push(e), + } + } + }); + + let gen = quote! { + impl CosmicConfigEntry for #name { + fn write_entry(&self, config: &Config) -> Result<(), cosmic_config::Error> { + #(#write_each_config_field)* + Ok(()) + } + + fn get_entry(config: &Config) -> Result, Self)> { + let mut default = Self::default(); + let mut errors = Vec::new(); + + #(#get_each_config_field)* + + if errors.is_empty() { + Ok(default) + } else { + Err((errors, default)) + } + } + } + }; + + gen.into() +} diff --git a/cosmic-config/Cargo.toml b/cosmic-config/Cargo.toml index 38cf3e41..d7af68ed 100644 --- a/cosmic-config/Cargo.toml +++ b/cosmic-config/Cargo.toml @@ -3,10 +3,16 @@ name = "cosmic-config" version = "0.1.0" edition = "2021" +[features] +default = ["macro"] +macro = ["cosmic-config-derive"] + [dependencies] atomicwrites = "0.4.0" calloop = { version = "0.10.5", optional = true } -dirs = "4.0.0" -notify = "5.1.0" +dirs = "5.0.1" +notify = "6.0.0" ron = "0.8.0" serde = "1.0.152" +cosmic-config-derive = { path = "../cosmic-config-derive/", optional = true } + diff --git a/cosmic-config/src/lib.rs b/cosmic-config/src/lib.rs index e4b237a1..b4964028 100644 --- a/cosmic-config/src/lib.rs +++ b/cosmic-config/src/lib.rs @@ -7,6 +7,9 @@ use std::{ sync::Mutex, }; +#[cfg(feature = "macro")] +pub use cosmic_config_derive; + #[cfg(feature = "calloop")] pub mod calloop; @@ -251,3 +254,9 @@ impl<'a> ConfigSet for ConfigTransaction<'a> { Ok(()) } } + +pub trait CosmicConfigEntry where Self: Sized + Default { + fn write_entry(&self, config: &Config) -> Result<(), crate::Error>; + fn get_entry(config: &Config) -> Result, Self)>; +} +