Skip to content

Commit

Permalink
Add #[config(partial_attr(...))] to add attributes for partial type
Browse files Browse the repository at this point in the history
CC #17
  • Loading branch information
LukasKalbertodt committed Dec 8, 2023
1 parent 830ca77 commit fa79a46
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.


## [Unreleased]
- Add `#[config(partial_attr(...))]` struct attribute to specify attributes for
the partial type.

## [0.2.4] - 2023-07-02
- Fixed enum deserialization from env values
Expand Down
1 change: 1 addition & 0 deletions examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct Conf {

/// Configuring the HTTP server of our app.
#[derive(Debug, Config)]
#[config(partial_attr(derive(Clone)))]
struct Http {
/// The port the server will listen on.
#[config(env = "PORT")]
Expand Down
3 changes: 3 additions & 0 deletions macro/src/gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,16 @@ fn gen_partial_mod(input: &ir::Input) -> TokenStream {
input.name,
);

let partial_attrs = &input.partial_attrs;

quote! {
#[doc = #module_doc]
#visibility mod #mod_name {
#![allow(missing_docs)]
use super::*;

#[derive(confique::serde::Deserialize)]
#( #[ #partial_attrs ])*
#struct_visibility struct #struct_name {
#( #struct_fields, )*
}
Expand Down
3 changes: 3 additions & 0 deletions macro/src/ir.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//! Definition of the intermediate representation.

use proc_macro2::TokenStream;


/// The parsed input to the `gen_config` macro.
pub(crate) struct Input {
pub(crate) doc: Vec<String>,
pub(crate) visibility: syn::Visibility,
pub(crate) partial_attrs: Vec<TokenStream>,
pub(crate) name: syn::Ident,
pub(crate) fields: Vec<Field>,
}
Expand Down
44 changes: 44 additions & 0 deletions macro/src/parse.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use proc_macro2::{TokenStream, Group, Delimiter, Ident};
use syn::{Error, Token, parse::{Parse, ParseStream}, spanned::Spanned, punctuated::Punctuated};

use crate::{
Expand All @@ -17,6 +18,7 @@ impl Input {
};

let doc = extract_doc(&mut input.attrs);
let partial_attrs = extract_struct_attrs(input.attrs)?;
let fields = fields.named.into_iter()
.map(Field::from_ast)
.collect::<Result<Vec<_>, _>>()?;
Expand All @@ -25,12 +27,54 @@ impl Input {
Ok(Self {
doc,
visibility: input.vis,
partial_attrs,
name: input.ident,
fields,
})
}
}

fn extract_struct_attrs(attrs: Vec<syn::Attribute>) -> Result<Vec<TokenStream>, Error> {
#[derive(Debug)]
enum StructAttr {
InternalAttr(TokenStream),
}

impl Parse for StructAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
syn::parenthesized!(content in input);
assert_empty_or_comma(input)?;

let name: Ident = content.parse()?;
match &*name.to_string() {
"partial_attr" => {
let g: Group = content.parse()?;
if g.delimiter() != Delimiter::Parenthesis {
return Err(Error::new_spanned(g,
"expected `(...)` but found different delimiter"));
}
assert_empty_or_comma(&content)?;
Ok(Self::InternalAttr(g.stream()))
}
_ => Err(Error::new_spanned(name, "unknown attribute")),
}
}
}

let mut partial_attrs = Vec::new();
for attr in attrs {
if !attr.path.is_ident("config") {
continue;
}
match syn::parse2::<StructAttr>(attr.tokens)? {
StructAttr::InternalAttr(tokens) => partial_attrs.push(tokens),
}
}

Ok(partial_attrs)
}

impl Field {
fn from_ast(mut field: syn::Field) -> Result<Self, Error> {
let doc = extract_doc(&mut field.attrs);
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,13 @@ pub use crate::{
/// the field.. Can only be present if the `env` attribute is present. Also
/// see [`env::parse`].
///
/// There are also the following attributes on the struct itself:
///
/// - **`#[config(partial_attr(...))]`: specify attributes that should be
/// attached to the partial struct definition. For example,
/// `#[config(partial_attr(derive(Clone)))]` can be used to make the partial
/// type implement `Clone`.
///
/// [serde-deser]: https://serde.rs/field-attrs.html#deserialize_with
///
/// ## Special types for leaf fields
Expand Down

0 comments on commit fa79a46

Please sign in to comment.