Compare commits

..

2 Commits

Author SHA1 Message Date
f5935b673b add doctest 2024-04-17 16:59:24 +02:00
29d466906f add package metadata 2024-04-17 16:59:06 +02:00
2 changed files with 48 additions and 3 deletions

View File

@ -4,9 +4,11 @@ version = "0.1.0"
edition = "2021" edition = "2021"
description = "macro for creating configurations using miniconf" description = "macro for creating configurations using miniconf"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://git.mkaenner.de/max/macroconf.git" repository = "https://git.mkaenner.de/max/macroconf"
readme = "README.md"
categories = ["config", "embedded", "data-structures"] categories = ["config", "embedded", "data-structures"]
keywords = ["miniconf", "macro", "config"] keywords = ["miniconf", "macro", "config"]
authors = ["Max Känner <max.kaenner@gmail.com>"]
[lints.rust] [lints.rust]
unsafe_code = "forbid" unsafe_code = "forbid"

View File

@ -1,8 +1,11 @@
//! This crate creates `miniconf::Tree` implementations fields in a struct. These carry some extra
//! extra information about the field.
use std::iter::once; use std::iter::once;
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::{Span as Span2, TokenStream as TokenStream2}; use proc_macro2::{Span as Span2, TokenStream as TokenStream2, TokenTree as TokenTree2};
use quote::{format_ident, quote, ToTokens}; use quote::{format_ident, quote, ToTokens};
use syn::{ use syn::{
parse_macro_input, parse_quote, Attribute, DataStruct, DeriveInput, Expr, ExprLit, Field, parse_macro_input, parse_quote, Attribute, DataStruct, DeriveInput, Expr, ExprLit, Field,
@ -12,6 +15,20 @@ use syn::{
/// Creates structs for the values to extend them with extra metadata. /// Creates structs for the values to extend them with extra metadata.
/// ///
/// supported metadata is `min`, `max` and `default`. Doc comments are parsed as `description` /// supported metadata is `min`, `max` and `default`. Doc comments are parsed as `description`
///
/// # Example
/// ```
/// use macroconf::config;
///
/// #[config]
/// struct Config {
/// /// This will be parsed as description
/// #[min] // This will use i32::MIN for the minimum
/// #[max = 50] // The value 50 is used for the maximum
/// #[default = 42] // A `Default` implementation will be generated returning 42
/// field1: i32,
/// }
/// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn config(_attr: TokenStream, item: TokenStream) -> TokenStream { pub fn config(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as DeriveInput); let mut input = parse_macro_input!(item as DeriveInput);
@ -37,6 +54,32 @@ pub fn config(_attr: TokenStream, item: TokenStream) -> TokenStream {
} }
} }
if let Some(attr) = input
.attrs
.iter_mut()
.find(|attr| attr.path().is_ident("derive"))
{
if let Ok(meta) = attr.meta.require_list() {
let derives_tree = meta
.tokens
.clone()
.into_iter()
.filter_map(|token| match token {
TokenTree2::Ident(ident) if ident == Ident::new("Tree", ident.span()) => {
Some(ident)
}
_ => None,
})
.count()
== 1;
if !derives_tree {
input.attrs.push(parse_quote!(#[derive(::miniconf::Tree)]));
}
}
} else {
input.attrs.push(parse_quote!(#[derive(::miniconf::Tree)]));
}
quote! { quote! {
#input #input
#(#new_types)* #(#new_types)*
@ -339,7 +382,7 @@ fn generate_serde(ident: &Ident, ty: &Type, checked_new: bool) -> TokenStream2 {
let conversion = if checked_new { let conversion = if checked_new {
quote! { quote! {
Self::new(value).ok_or_else(|| { Self::new(value).ok_or_else(|| {
D::Error::custom("checking value bounds") <D::Error as ::serde::de::Error>::custom("checking value bounds")
}) })
} }
} else { } else {