feat: derive macro for loading a cosmic-config

This commit is contained in:
Ashley Wulber 2023-05-19 15:36:15 -04:00
parent f433b3dc20
commit 76fd9207e5
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
5 changed files with 98 additions and 2 deletions

View file

@ -76,6 +76,7 @@ optional = true
[workspace]
members = [
"cosmic-config",
"cosmic-config-derive",
"examples/*",
]
exclude = [

View file

@ -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"

View file

@ -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, (Vec<cosmic_config::Error>, 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()
}

View file

@ -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 }

View file

@ -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, (Vec<crate::Error>, Self)>;
}