Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: Use an enum + strum instead the Symbol struct #69

Merged
merged 6 commits into from
Dec 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub struct ResourceLimits {
// ...
}
#[derive(Builder)]
#[group(program = at_least(1))]
#[groups(program = at_least(1))]
pub struct Launchd {
#[builder(mandatory)]
label: Option<String>,
Expand Down Expand Up @@ -93,14 +93,14 @@ This is a quick overview of the features in this library. See [`const_typed_buil
- `#[builder(assume_mandatory)]`: Indicates that all fields in the struct should be assumed as mandatory.
If provided without an equals sign (e.g., `#[builder(assume_mandatory)]`), it sets the `mandatory` flag for fields to true.
If provided with an equals sign (e.g., `#[builder(assume_mandatory = true)]`), it sets the `mandatory` flag for fields based on the value.
- `#[group(group_name = (exact(N)|at_least(N)|at_most(N)|single)]`:
- `#[groups(group_name = (exact(N)|at_least(N)|at_most(N)|single)]`:
Associates fields of the struct with a group named "group_name" and specifies the group's behavior.
The `group_name` should be a string identifier. The group can have one of the following behaviors:
- `exact(N)`: Exactly N fields in the group must be set during the builder construction.
- `at_least(N)`: At least N fields in the group must be set during the builder construction.
- `at_most(N)`: At most N fields in the group can be set during the builder construction.
- `single`: Only one field in the group can be set during the builder construction. This is a shorthand for `exact(1)`.
e.g `#[group(foo = at_least(2))]` creates a group where at least 2 of the fields need to be initialized.
e.g `#[groups(foo = at_least(2))]` creates a group where at least 2 of the fields need to be initialized.
- `#[builder(solver = (brute_force|compiler))]`: **Use sparingly, see note at bottom of this file!**
Specifies the solver type to be used for building the struct. The `solve_type` should be one of the predefined solver types, such as `brute_force` or `compiler`. If provided with an equals sign (e.g., `#[builder(solver = brute_force)]`),
it sets the "solver type" accordingly. This attribute is still tested, and `brute_force` is the default, and only if there are problems in compilation time then you can try `compiler`. `compiler` gives less guarantees though.
Expand Down
1 change: 1 addition & 0 deletions const_typed_builder_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ proc-macro2 = "1.0.70"
itertools = "0.12.0"
convert_case = "0.6.0"
proc-macro-error = "1.0.4"
strum = { version = "0.25.0", features = ["derive"] }

[lib]
proc-macro = true
8 changes: 4 additions & 4 deletions const_typed_builder_derive/src/generator/builder_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::info::{
Container, Field, FieldKind, GroupType, SetterKind, SolverKind, TrackedField, TrackedFieldKind,
};
use itertools::{Itertools, Powerset};
use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use proc_macro_error::emit_error;
use quote::{quote, ToTokens};
use std::{collections::BTreeSet, ops::Deref};
Expand Down Expand Up @@ -314,11 +314,11 @@ Setter for the [`{}::{field_ident}`] field.

let all = self.info.group_collection().values().map(|group| {
let partials = group.indices().iter().map(|index| self.info.field_collection().get(*index).expect("Could not find field associated to group").const_ident());
let function_call: syn::Ident = group.function_symbol().into();
let function_call = syn::Ident::new(group.function_symbol().as_ref(), Span::call_site());
let count = group.expected_count();
let ident = group.ident();
let function_ident = group.function_symbol().to_string();
let err_text = format!("`.build()` failed because the bounds of group `{ident}` where not met ({function_ident} {count})");
let function_name = group.function_symbol();
let err_text = format!("`.build()` failed because the bounds of group `{ident}` where not met ({function_name} {count})");

quote!(
if !Self::#function_call(&[#(#partials),*], #count) {
Expand Down
8 changes: 4 additions & 4 deletions const_typed_builder_derive/src/info/group.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::symbol::{Symbol, AT_LEAST, AT_MOST, EXACT};
use crate::symbol::Symbol;
use proc_macro_error::{emit_error, emit_warning};
use std::{
cmp::Ordering,
Expand Down Expand Up @@ -62,9 +62,9 @@ impl Group {
/// Retrieves the function symbol associated with the group type.
pub fn function_symbol(&self) -> Symbol {
match self.group_type {
GroupType::Exact(_) => EXACT,
GroupType::AtLeast(_) => AT_LEAST,
GroupType::AtMost(_) => AT_MOST,
GroupType::Exact(_) => Symbol::Exact,
GroupType::AtLeast(_) => Symbol::AtLeast,
GroupType::AtMost(_) => Symbol::AtMost,
}
}

Expand Down
2 changes: 1 addition & 1 deletion const_typed_builder_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use syn::DeriveInput;
///
/// This will generate a builder pattern for `MyStruct`, allowing you to
/// construct instances of `MyStruct` with a fluent API.
#[proc_macro_derive(Builder, attributes(builder, group))]
#[proc_macro_derive(Builder, attributes(builder, group, groups))]
#[proc_macro_error]
pub fn derive_builder(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(input as DeriveInput);
Expand Down
76 changes: 52 additions & 24 deletions const_typed_builder_derive/src/parser/container_parser.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use super::{FieldParser, GroupParser};
use crate::{
info::{Container, Field, FieldCollection, GroupCollection, SolverKind},
symbol,
symbol::Symbol,
};
use proc_macro_error::{emit_call_site_error, emit_error, emit_warning};
use std::str::FromStr;

/// Represents the parser for struct generation.
#[derive(Debug)]
Expand Down Expand Up @@ -82,10 +83,25 @@ impl ContainerParser {
note = err
),
}
match (&attr_ident.to_string()).into() {
symbol::GROUP => GroupParser::new(&mut self.groups).parse(attr),
symbol::BUILDER => self.handle_attribute_builder(attr),
_ => emit_error!(&attr, "Unknown attribute"),
match Symbol::from_str(&attr_ident.to_string()) {
Ok(symbol) => match symbol {
Symbol::Group => {
emit_warning!(&attr_ident, "The use of group as a top level attribute is being deprecated, use groups instead");
GroupParser::new(&mut self.groups).parse(attr)
}
Symbol::Groups => GroupParser::new(&mut self.groups).parse(attr),
Symbol::Builder => self.handle_attribute_builder(attr),
symbol => {
emit_error!(
&attr.meta,
format!("Attribute {symbol} can't be used at container level")
)
}
},
Err(err) => emit_error!(
&attr_ident, "Unknown symbol";
note = err
),
}
}

Expand All @@ -110,31 +126,43 @@ impl ContainerParser {
Err(err) => {
emit_error!(
&attr.meta, "Specifier cannot be parsed";
help = "Try specifying it like #[{}(specifier)]", symbol::BUILDER;
help = "Try specifying it like #[{}(specifier)]", Symbol::Builder;
note = err
);
return Ok(());
}
};

match (&path_ident.to_string()).into() {
symbol::ASSUME_MANDATORY => {
self.assume_mandatory = true;
}
symbol::INTO => {
self.assume_into = true;
}
symbol::SOLVER => {
let syn::ExprPath { path, .. } = meta.value()?.parse()?;
match (&path.require_ident()?.to_string()).into() {
symbol::BRUTE_FORCE => self.solver_kind = SolverKind::BruteForce,
symbol::COMPILER => self.solver_kind = SolverKind::Compiler,
_ => emit_error!(&path, "Unknown solver type"),
match Symbol::from_str(&path_ident.to_string()) {
Ok(symbol) => match symbol {
Symbol::Solver => {
let syn::ExprPath { path, .. } = meta.value()?.parse()?;
match Symbol::from_str(&path.require_ident()?.to_string()) {
Ok(solver) => match solver {
Symbol::BruteForce => self.solver_kind = SolverKind::BruteForce,
Symbol::Compiler => self.solver_kind = SolverKind::Compiler,
solver => {
emit_error!(&path, format!("{solver} is not a solver type"))
}
},
Err(err) => emit_error!(
&path, "Unknown symbol";
note = err
),
}
}
}
_ => {
emit_error!(meta.path, "Unknown attribute");
}
Symbol::AssumeMandatory => self.assume_mandatory = true,
Symbol::Into => self.assume_into = true,
symbol => {
emit_error!(
&attr.meta,
format!("Specifier {symbol} can't be used at container level")
)
}
},
Err(err) => emit_error!(
&attr.meta, "Unknown symbol";
note = err
),
}
Ok(())
})
Expand Down
37 changes: 23 additions & 14 deletions const_typed_builder_derive/src/parser/field_parser.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::{
info::{Field, FieldKind, GroupCollection, SetterKind},
symbol,
symbol::Symbol,
util::is_option,
};
use proc_macro_error::{emit_error, emit_warning};
use std::str::FromStr;

/// Represents settings for struct field generation.
#[derive(Debug)]
Expand Down Expand Up @@ -97,7 +98,7 @@ impl<'parser> FieldParser<'parser> {
///
fn handle_attribute(&mut self, attr: &syn::Attribute) {
let attr_ident = match attr.path().require_ident() {
Ok(ident) if ident == symbol::BUILDER => ident,
Ok(ident) if Symbol::from_str(&ident.to_string()) == Ok(Symbol::Builder) => ident,
Ok(ident) => {
emit_error!(ident, format!("{ident} can't be used as a field attribute"));
return;
Expand Down Expand Up @@ -137,17 +138,25 @@ impl<'parser> FieldParser<'parser> {
}
};

match (&path_ident.to_string()).into() {
symbol::SKIP => self.handle_attribute_skip(path_ident),
symbol::MANDATORY => self.handle_attribute_mandatory(path_ident),
symbol::OPTIONAL => self.handle_attribute_optional(path_ident),
symbol::GROUP => self.handle_attribute_group(&meta),
symbol::PROPAGATE => self.handle_setter_kind(SetterKind::Propagate, path_ident),
symbol::ASREF => self.handle_setter_kind(SetterKind::AsRef, path_ident),
symbol::ASMUT => self.handle_setter_kind(SetterKind::AsMut, path_ident),
symbol::INTO => self.handle_setter_kind(SetterKind::Into, path_ident),
symbol::STANDARD => self.handle_setter_kind(SetterKind::Standard, path_ident),
_ => emit_error!(&attr.meta, "Unknown attribute"),
match Symbol::from_str(&path_ident.to_string()) {
Ok(symbol) => match symbol {
Symbol::Skip => self.handle_attribute_skip(path_ident),
Symbol::Mandatory => self.handle_attribute_mandatory(path_ident),
Symbol::Optional => self.handle_attribute_optional(path_ident),
Symbol::Group => self.handle_attribute_group(&meta),
Symbol::Propagate => self.handle_setter_kind(SetterKind::Propagate, path_ident),
Symbol::AsRef => self.handle_setter_kind(SetterKind::AsRef, path_ident),
Symbol::AsMut => self.handle_setter_kind(SetterKind::AsMut, path_ident),
Symbol::Into => self.handle_setter_kind(SetterKind::Into, path_ident),
Symbol::Standard => self.handle_setter_kind(SetterKind::Standard, path_ident),
symbol => {
emit_error!(&attr.meta, format!("Specifier {symbol} can't be used here"))
}
},
Err(err) => emit_error!(
&attr.meta, "Unknown attribute";
note = err
),
}
Ok(())
})
Expand Down Expand Up @@ -263,7 +272,7 @@ impl<'parser> FieldParser<'parser> {
Err(err) => {
emit_error!(
meta.path, "Group name not specified correctly";
help = "Try defining it like #[{}(foo)]", symbol::BUILDER;
help = "Try defining it like #[{}(foo)]", Symbol::Builder;
note = err
);
}
Expand Down
Loading