diff --git a/Cargo.toml b/Cargo.toml index 628262c..0c44471 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,23 @@ name = "from_file" version = "0.1.0" edition = "2024" +authors = ["Kristofers Solo "] +description = "Procedural macro to derive configuration from files, with optional merging capabilities." +repository = "https://github.com/kristoferssolo/from_file" +documentation = "https://docs.rs/from_file" +homepage = "https://github.com/kristoferssolo/from_file" +license = "MIT OR Apache-2.0" +keywords = [ + "proc-macro", + "derive", + "configuration", + "file-parsing", + "defaults", + "merge", +] +categories = ["development-tools::procedural-macro", "parsing", "config"] +exclude = ["/.github", "/.gitignore", "/tests", "*.png", "*.md"] +readme = "README.md" [features] default = [] @@ -17,3 +34,8 @@ merge = { version = "0.2", optional = true } [lib] proc-macro = true + +[lints.clippy] +pedantic = "warn" +nursery = "warn" +unwrap_used = "warn" diff --git a/README.md b/README.md new file mode 100644 index 0000000..7285f8b --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# `from_file` + +Procedural macro to derive configuration from files, with optional merging capabilities. + +## Features + +- **Derive Configuration:** Easily load configuration from files into your Rust structs. +- **Default Values:** Specify default values for struct fields using the `#[default = "..."]` attribute. +- **Optional Merging:** When the `merge` feature is enabled, allows merging multiple configuration sources. + +## Usage + +```sh +cargo add hexlab +``` + +Example: + +```rust +use from_file::FromFile; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Default, Deserialize, Serialize, FromFile)] +pub struct MyConfig { + pub host: String, + #[default = "8080"] + pub port: u16, + #[default = "false"] + pub enabled: bool, +} + +fn main() { + // Simulate loading from a file (e.g., JSON, YAML) + let file_content = r#" + { + "host": "localhost" + } + "#; + + let config_from_file: MyConfig = serde_json::from_str(file_content).unwrap(); + let config = MyConfig::from_file(Some(config_from_file)); + + println!("Config: {:?}", config); + // Expected output: Config { host: "localhost", port: 8080, enabled: false } +} +``` + +## Documentation + +Full documentation is available at [docs.rs](https://docs.rs/from_file). + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## License + +This project is dual-licensed under either: + +- MIT License ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) + +at your option. diff --git a/src/from_file.rs b/src/from_file.rs index 9f3db8d..df631a0 100644 --- a/src/from_file.rs +++ b/src/from_file.rs @@ -1,8 +1,8 @@ -use proc_macro2::TokenStream; +use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{ Attribute, Data, DeriveInput, Error, Expr, Fields, GenericParam, Generics, Meta, Result, - parse_quote, + WherePredicate, parse_quote, parse2, }; const WITH_MERGE: bool = cfg!(feature = "merge"); @@ -78,8 +78,11 @@ pub fn impl_from_file(input: &DeriveInput) -> Result { } else { let mut where_clause = where_clause.cloned(); if let Some(wc) = &mut where_clause { - wc.predicates - .extend(default_bounds.into_iter().map(|bound| parse_quote!(#bound))); + wc.predicates.extend( + default_bounds + .into_iter() + .map(|bound| parse2::(bound).unwrap()), + ); } else { where_clause = Some(parse_quote!(where #(#default_bounds),*)); } @@ -117,8 +120,7 @@ pub fn impl_from_file(input: &DeriveInput) -> Result { Self::from_file(value) } } - } - .into()) + }.into()) } fn add_trait_bouts(mut generics: Generics) -> Generics { @@ -136,9 +138,8 @@ fn parse_default_attr(attr: &Attribute) -> Result> { } let meta = attr.parse_args::()?; - let name_value = match meta { - Meta::NameValue(nv) => nv, - _ => return Err(Error::new_spanned(attr, "Expected #[default = \"value\"]")), + let Meta::NameValue(name_value) = meta else { + return Err(Error::new_spanned(attr, "Expected #[default = \"value\"]")); }; match name_value.value { diff --git a/src/lib.rs b/src/lib.rs index 80ba3b4..2b6c4bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,5 @@ use syn::{DeriveInput, parse_macro_input}; #[proc_macro_derive(FromFile, attributes(from_file))] pub fn derive_from_file(input: TokenStream) -> TokenStream { let inp = parse_macro_input!(input as DeriveInput); - match impl_from_file(&inp) { - Ok(ts) => ts.into(), - Err(e) => e.to_compile_error().into(), - } + impl_from_file(&inp).unwrap_or_else(|e| e.to_compile_error().into()) }