• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Alorel / macroific-rs / 11197184821

05 Oct 2024 11:43PM CUT coverage: 81.468%. First build
11197184821

Pull #14

github

web-flow
Merge c8342d59b into 697e66c3a
Pull Request #14: v2

430 of 476 new or added lines in 16 files covered. (90.34%)

1099 of 1349 relevant lines covered (81.47%)

46.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.22
/modules/macroific-macro/src/attr_parse/mod.rs
1
use std::fmt::Write;
2

3
use proc_macro2::TokenStream;
4
use proc_macro2::{Delimiter, Group, Ident, Punct};
5
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
6
use syn::parse::{Parse, ParseStream};
7
use syn::{parse_macro_input, Generics};
8

9
pub use attr_options::AttrOptionsDerive;
10
use macroific_core::core_ext::{MacroificCoreIdentExt, MacroificCorePunctExt};
11
use macroific_core::elements::module_prefix::{OPTION, RESULT};
12
use macroific_core::elements::{GenericImpl, ModulePrefix};
13
use options::*;
14
pub use parse_option::ParseOptionDerive;
15

16
use crate::BaseTokenStream;
17

18
mod attr_options;
19
mod options;
20
mod parse_option;
21

22
const ATTR_NAME: &str = "attr_opts";
23

24
const PRIVATE: ModulePrefix<'static, 3> =
25
    ModulePrefix::new(["macroific", "attr_parse", "__private"]);
26
const BASE: ModulePrefix<'static, 2> = ModulePrefix::new(["macroific", "attr_parse"]);
27

28
trait Render {
29
    const TRAIT_NAME: &'static str;
30

31
    fn generics(&self) -> &Generics;
32
    fn ident(&self) -> &Ident;
33
    fn fields(&self) -> &Fields;
34

35
    fn render_empty_body(ending: Option<Group>) -> TokenStream;
36

37
    #[inline]
38
    fn impl_generics(&self) -> TokenStream {
14✔
39
        impl_generics(self.generics(), self.ident(), Self::TRAIT_NAME)
14✔
40
    }
14✔
41

42
    fn named_fields(&self) -> Result<&[Field], Option<Delimiter>> {
12✔
43
        match *self.fields() {
12✔
44
            Fields::Named(ref fields) => Ok(fields),
12✔
45
            Fields::Empty(delim) => Err(Some(delim)),
×
46
            Fields::Unit => Err(None),
×
47
        }
48
    }
12✔
49

NEW
50
    fn render_empty(&self, delimiter: Option<Delimiter>) -> TokenStream {
×
NEW
51
        let ending = empty_ending(delimiter);
×
NEW
52
        let mut tokens = self.impl_generics();
×
NEW
53

×
NEW
54
        tokens.append(Group::new(
×
NEW
55
            Delimiter::Brace,
×
NEW
56
            Self::render_empty_body(ending),
×
NEW
57
        ));
×
NEW
58

×
NEW
59
        tokens
×
NEW
60
    }
×
61
}
62

63
fn nones(fields: &[Field]) -> TokenStream {
12✔
64
    (0..fields.len())
12✔
65
        .map(move |idx| {
210✔
66
            let ident = field_ident_at(idx);
210✔
67
            quote! { let mut #ident = #OPTION::None; }
210✔
68
        })
210✔
69
        .collect()
12✔
70
}
12✔
71

72
fn unwraps<'a>(
12✔
73
    indexed_fields: impl Iterator<Item = IndexedFieldTuple<'a>>,
12✔
74
    span_arg_name: &impl ToTokens,
12✔
75
) -> Group {
12✔
76
    let body = indexed_fields.map(move |(option_var_name, field)| {
210✔
77
        let mut out = field.ident.to_token_stream();
210✔
78
        out.append(Punct::new_joint(':'));
210✔
79

80
        match field.opts.default {
197✔
81
            None | Some(DefaultOption::Implicit | DefaultOption::Explicit(true)) => {
13✔
82
                out.extend(quote! { #option_var_name.unwrap_or_default() });
13✔
83
            }
13✔
84
            Some(DefaultOption::Explicit(false)) => {
85
                let mut missing_field_err = String::from("Missing required attribute: ");
196✔
86
                if let Some(ref rename) = field.opts.rename {
196✔
87
                    write!(&mut missing_field_err, "{}", rename.token()).unwrap();
1✔
88
                } else {
195✔
89
                    write!(&mut missing_field_err, "{}", field.ident).unwrap();
195✔
90
                }
195✔
91

92
                out.extend(quote! { if let #OPTION::Some(v) = #option_var_name {
196✔
93
                    v
196✔
94
                } else {
196✔
95
                    return #RESULT::Err(::syn::Error::new(#span_arg_name, #missing_field_err));
196✔
96
                } });
196✔
97
            }
98
            Some(DefaultOption::Path(ref path)) => {
1✔
99
                out.extend(quote! { #option_var_name.unwrap_or_else(#path) });
1✔
100
            }
1✔
101
        };
102

103
        out.append(Punct::new_joint(','));
210✔
104

210✔
105
        out
210✔
106
    });
210✔
107

12✔
108
    Group::new(Delimiter::Brace, body.collect())
12✔
109
}
12✔
110

111
type IndexedFieldTuple<'a> = (Ident, &'a Field);
112

113
fn indexed_fields(fields: &[Field]) -> impl Iterator<Item = IndexedFieldTuple> + Clone {
12✔
114
    fields.iter().enumerate().map(move |(idx, field)| {
420✔
115
        let option_var_name = field_ident_at(idx);
420✔
116

420✔
117
        (option_var_name, field)
420✔
118
    })
420✔
119
}
12✔
120

121
fn empty_ending(delimiter: Option<Delimiter>) -> Option<Group> {
×
122
    delimiter.map(move |d| Group::new(d, TokenStream::new()))
×
123
}
×
124

125
pub fn run<T: Parse + ToTokens>(input: BaseTokenStream) -> BaseTokenStream {
14✔
126
    parse_macro_input!(input as T).into_token_stream().into()
14✔
127
}
14✔
128

129
fn impl_generics(generics: &Generics, ident: &Ident, trait_name: &str) -> TokenStream {
14✔
130
    let impl_trait = {
14✔
131
        let trait_name = Ident::create(trait_name);
14✔
132
        quote!(#BASE::#trait_name)
14✔
133
    };
14✔
134
    let mut tokens = quote!(#[automatically_derived]);
14✔
135

14✔
136
    GenericImpl::new(generics)
14✔
137
        .with_trait(impl_trait)
14✔
138
        .with_target(ident)
14✔
139
        .to_tokens(&mut tokens);
14✔
140

14✔
141
    tokens
14✔
142
}
14✔
143

144
fn field_ident_at(idx: usize) -> Ident {
630✔
145
    format_ident!("field{idx}")
630✔
146
}
630✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc