Skip to content

Commit d097616

Browse files
committed
Add #[bool] attribute to #[config_type]
This allows declaring a boolean value as aliasing an enum variant, for upgrading fields that were previously boolean valued into an enum.
1 parent 3454812 commit d097616

File tree

3 files changed

+64
-72
lines changed

3 files changed

+64
-72
lines changed

Diff for: config_proc_macro/src/attrs.rs

+24
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,30 @@ pub fn is_unstable_variant(attr: &syn::Attribute) -> bool {
5050
is_attr_path(attr, "unstable_variant")
5151
}
5252

53+
/// Returns the value of the first `bool` attribute in the given slice or
54+
/// `None` if `bool` attribute is not available.
55+
pub fn find_config_bool(attrs: &[syn::Attribute]) -> Option<bool> {
56+
attrs.iter().filter_map(config_bool).next()
57+
}
58+
59+
/// Returns a bool literal value if the given attribute is `bool`
60+
/// attribute or `None` otherwise.
61+
pub fn config_bool(attr: &syn::Attribute) -> Option<bool> {
62+
match &attr.meta {
63+
syn::Meta::NameValue(syn::MetaNameValue {
64+
path,
65+
value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Bool(lit_bool), .. }),
66+
..
67+
}) if path.is_ident("bool") => Some(lit_bool.value()),
68+
_ => None,
69+
}
70+
}
71+
72+
/// Returns `true` if the given attribute is a `bool` attribute.
73+
pub fn is_config_bool(attr: &syn::Attribute) -> bool {
74+
is_attr_name_value(attr, "bool")
75+
}
76+
5377
fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool {
5478
match &attr.meta {
5579
syn::Meta::NameValue(syn::MetaNameValue { path, .. }) if path.is_ident(name) => true,

Diff for: config_proc_macro/src/item_enum.rs

+36-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fn process_variant(variant: &syn::Variant) -> TokenStream {
4848
let metas = variant
4949
.attrs
5050
.iter()
51-
.filter(|attr| !is_doc_hint(attr) && !is_config_value(attr) && !is_unstable_variant(attr));
51+
.filter(|attr| !is_doc_hint(attr) && !is_config_value(attr) && !is_unstable_variant(attr) && !is_config_bool(attr));
5252
let attrs = fold_quote(metas, |meta| quote!(#meta));
5353
let syn::Variant { ident, fields, .. } = variant;
5454
quote!(#attrs #ident #fields)
@@ -123,12 +123,22 @@ fn impl_from_str(ident: &syn::Ident, variants: &Variants) -> TokenStream {
123123
let vs = variants
124124
.iter()
125125
.filter(|v| is_unit(v))
126-
.map(|v| (config_value_of_variant(v), &v.ident));
127-
let if_patterns = fold_quote(vs, |(s, v)| {
126+
.map(|v| (config_value_of_variant(v), &v.ident, find_config_bool(&v.attrs)));
127+
let if_patterns = fold_quote(vs, |(s, v, b)| {
128+
let parse_bool = b.map(|b| {
129+
let b = if b { "true" } else { "false" };
130+
quote! {
131+
if #b == s {
132+
return Ok(#ident::#v);
133+
}
134+
}
135+
}).unwrap_or_default();
136+
128137
quote! {
129138
if #s.eq_ignore_ascii_case(s) {
130139
return Ok(#ident::#v);
131140
}
141+
#parse_bool
132142
}
133143
});
134144
let mut err_msg = String::from("Bad variant, expected one of:");
@@ -210,6 +220,27 @@ fn impl_deserialize(ident: &syn::Ident, variants: &Variants) -> TokenStream {
210220
let supported_vs = variants.iter().filter(|v| is_unit(v));
211221
let allowed = fold_quote(supported_vs.map(config_value_of_variant), |s| quote!(#s,));
212222

223+
let mut bools =
224+
variants.iter()
225+
.filter(|v| is_unit(v))
226+
.filter_map(|v| Some({
227+
let bool = find_config_bool(&v.attrs)?;
228+
let value = config_value_of_variant(v);
229+
quote! {
230+
#bool => return Ok(String::from(#value)),
231+
}
232+
})).peekable();
233+
234+
let visit_bool = bools.peek().is_some().then(|| {
235+
quote! {
236+
fn visit_bool<E>(self, value: bool) -> Result<String, E> {
237+
match value {
238+
#(#bools)*
239+
}
240+
}
241+
}
242+
}).unwrap_or_default();
243+
213244
quote! {
214245
impl<'de> serde::de::Deserialize<'de> for #ident {
215246
fn deserialize<D>(d: D) -> Result<Self, D::Error>
@@ -229,7 +260,9 @@ fn impl_deserialize(ident: &syn::Ident, variants: &Variants) -> TokenStream {
229260
fn visit_str<E>(self, value: &str) -> Result<String, E> {
230261
Ok(String::from(value))
231262
}
263+
#visit_bool
232264
}
265+
233266
let s = &d.deserialize_string(StringOnly::<D>(PhantomData))?;
234267

235268
#if_patterns

Diff for: src/config/options.rs

+4-69
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,14 @@ pub enum Verbosity {
217217
}
218218

219219
/// Which comments to wrap
220-
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
220+
#[config_type]
221+
#[derive(Copy)]
221222
pub enum WrapComments {
222223
/// Don't wrap comments
224+
#[bool = false]
223225
Off,
224226
/// Wrap all kinds of comments
227+
#[bool = true]
225228
All,
226229
/// Only wrap doc comments
227230
Doc,
@@ -239,74 +242,6 @@ impl WrapComments {
239242
}
240243
}
241244

242-
impl fmt::Display for WrapComments {
243-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244-
match self {
245-
WrapComments::Off => f.write_str("Off"),
246-
WrapComments::All => f.write_str("All"),
247-
WrapComments::Doc => f.write_str("Doc"),
248-
WrapComments::Normal => f.write_str("Normal"),
249-
}
250-
}
251-
}
252-
253-
impl super::ConfigType for WrapComments {
254-
fn doc_hint() -> String {
255-
"[Off|All|Doc|Normal]".to_owned()
256-
}
257-
258-
fn stable_variant(&self) -> bool {
259-
true
260-
}
261-
}
262-
263-
impl std::str::FromStr for WrapComments {
264-
type Err = &'static str;
265-
266-
fn from_str(s: &str) -> Result<Self, Self::Err> {
267-
match s.to_lowercase().as_str() {
268-
"off" | "false" => Ok(WrapComments::Off),
269-
"all" | "true" => Ok(WrapComments::All),
270-
"doc" => Ok(WrapComments::Doc),
271-
"normal" => Ok(WrapComments::Normal),
272-
_ => Err("Bad variant, expected one of: `Off` `All` `Doc` `Normal`"),
273-
}
274-
}
275-
}
276-
277-
impl<'de> serde::de::Deserialize<'de> for WrapComments {
278-
fn deserialize<D>(d: D) -> Result<Self, D::Error>
279-
where
280-
D: serde::Deserializer<'de>,
281-
{
282-
use serde::de::Error;
283-
284-
struct StringOrBoolVisitor;
285-
286-
impl<'de> Visitor<'de> for StringOrBoolVisitor {
287-
type Value = String;
288-
289-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
290-
formatter.write_str("string")
291-
}
292-
293-
fn visit_str<E>(self, value: &str) -> Result<String, E> {
294-
Ok(String::from(value))
295-
}
296-
297-
fn visit_bool<E>(self, value: bool) -> Result<String, E> {
298-
Ok(value.to_string())
299-
}
300-
}
301-
302-
let s = d.deserialize_string(StringOrBoolVisitor)?;
303-
s.parse().map_err(|_| {
304-
static ALLOWED: &'static [&str] = &["Off", "All", "Doc", "Normal"];
305-
D::Error::unknown_variant(&s, ALLOWED)
306-
})
307-
}
308-
}
309-
310245
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
311246
pub struct WidthHeuristics {
312247
// Maximum width of the args of a function call before falling back

0 commit comments

Comments
 (0)