Expand description
§⛏️ fieldwork - field accessor generation
fieldwork
is a procedural macro crate designed to automate the generation of field accessor
methods for structs. By leveraging Rust’s powerful macro system, fieldwork
reduces
boilerplate code, enhances code readability, and ensures consistency. Just as importantly,
fieldwork
intends to be fully customizable and expressive for common access patterns.
Manually writing getters and setters for struct fields is repetitive, and adds to maintenance
burden. fieldwork
addresses this by providing a procedural macro that automatically generates
these methods based on your struct definitions. The intent of this crate, and distinguishing
feature, is to be as customizable and expressive as writing your own getters and setters. The crate
succeeds if you are able to emit exactly the code that you would have manually written, but far more
concisely.
Although this crate is fully configurable and these docs carefully describe the many configuration settings, wherever possible, fieldwork tries to express common patterns as the default.
§Performance
The compile time cost of using a proc macro crate is always worth considering. All efforts have been made to keep this crate as lightweight as possible and featureful enough to be worth the tradeoff.
§Testing
This crate has a full suite of macro-expansion tests in tests/expand. These tests are also used for test coverage.
§Configuration
fieldwork
supports four layers of configuration, from broadest to most specific: struct
configuration, struct method configuration,
field configuration and field method
configuration. The most specific configuration always has precedence.
§Quick Links (TOC)
Methods: get
, set
, get_mut
, with
,
without
, take
Struct configuration: vis
,
where_clause
, option_borrow_inner
,
deref
, option_set_some
, into
,
rename_predicates
Struct method configuration: vis
,
doc_template
, template
,
chain
, option_borrow_inner
,
deref
, option_set_some
,
into
, copy
Field configuration: skip
, name
,
argument
, vis
, deref
,
option_set_some
, into
,
option_borrow_inner
Field method configuration: name
,
argument
, doc
, chain
,
copy
, skip
, deref
,
option_set_some
, into
,
option_borrow_inner
How fieldwork selects which methods to generate for which fields
§Example to get a sense of the library
use std::path::PathBuf;
#[derive(fieldwork::Fieldwork, Default)]
#[fieldwork(get, set, with, without, get_mut, take, into, rename_predicates)]
struct ServerConfig {
/// server hostname
host: String,
/// server port
#[field(into = false)]
port: u16,
/// path to SSL certificate file
ssl_cert: Option<PathBuf>,
/// path to log directory
log_dir: Option<PathBuf>,
/// whether TLS is required
tls_required: bool,
/// whether verbose logging is enabled
verbose: bool,
#[field = false]
_runtime_data: (),
}
// Usage examples:
let mut config = ServerConfig::default()
.with_host("LocalHost") // accepts &str via Into<String>
.with_port(8080)
.with_log_dir("/var/log") // accepts &str, wraps in Some() automatically
.with_tls_required() // sets to true
.without_verbose(); // sets to false
config.host_mut().make_ascii_lowercase();
// Getters use idiomatic naming
assert_eq!(config.host(), "localhost");
assert_eq!(config.port(), 8080);
assert_eq!(config.log_dir().unwrap(), PathBuf::from("/var/log"));
assert!(config.is_tls_required()); // boolean getters use is_ prefix because of `rename_predicates`
assert!(!config.is_verbose());
// Chainable setters return &mut Self
config
.set_ssl_cert(PathBuf::from("/etc/ssl/cert.pem"))
.set_port(9090)
.set_verbose(true);
let cert = config.take_ssl_cert();
assert_eq!(cert, Some(PathBuf::from("/etc/ssl/cert.pem")));
// Without methods provide convenient clearing
config = config.without_log_dir(); // Sets log_dir to None
assert!(config.log_dir().is_none());
§General notes and configuration
§#[field]
and #[fieldwork]
attributes are used to configure fieldwork
Fieldwork can be configured on structs and fields through #[field]
and #[fieldwork]
,
interchangeably. These docs generally use #[fieldwork]
for the structs and #[field]
for the
indiviual fields.
§Fieldwork has four configuration levels that cascade
Configuration at each field method inherits from the field’s configuration, the struct configuration for that method, and the struct’s top level configuration. The most specific configuration always takes precedence. The intent of this approach is to avoid duplication and do what you intend.
§Boolean handling
#[field(option_borrow_inner = true)]
is the same as #[field(option_borrow_inner)]
and
this is the case anywhere booleans are accepted.
§Type quoting
Some types will need to be quoted if they contain lifetimes, brackets, or generics. Simple path
types like std::sync::Arc
do not need to be quoted.
§Common dereference types are detected by default
Cwned types that Deref
to commonly used borrowed types will automatically get detected and
dereferenced as such. So for example, a field that contains a String
will return &str
from get
or &mut str
from get_mut
by default. This behavior can be opted out of, at any configuration
level.
Owned Type | Borrowed Type | DerefMut |
---|---|---|
String | &str | yes |
OsString | &OsStr | yes |
Vec<T> | &[T] | yes |
Box<T> | &T | yes |
Arc<T> | &T | no |
Rc<T> | &T | no |
PathBuf | &Path | yes |
Cow<T> | &T | no |
[T; N] | &[T] | yes |
§Common copy types are detected by default
The current list of types that are detected are: bool
, char
, numeric primitives, and immutable
references (&T
). Almost always, if a getter returns a &bool
, the caller will want to dereference
that immediately, so by default get
returns those types by copy. This behavior can be opted out of
at the struct method level with #[fieldwork(get(copy = false))]
or at the field method level with
the same invocation.
§Options are returned as_ref
or as_deref
by default
By default, fieldwork detects Options and calls as_deref
or as_ref
on them, so instead of getting
&Option<String>
, you get Option<&str>
by default. It is possible to opt out of the option
detection behavior and the deref detection behavior distinctly, so you can have it return
Option<&String>
or &Option<String>
, at any configuration level.
§Option setters can automatically wrap values in Some
When option_set_some
is enabled, set
and with
methods for Option<T>
fields will accept T
as input and automatically wrap it in Some(T)
. This is useful for builder patterns and situations
where None
represents an unset default value that is only ever replaced with populated
values. Instead of calling user.set_name(Some("Alice".to_string()))
, you can simply call
user.set_name("Alice".to_string())
. This feature can be enabled at any configuration level and
only affects setter methods - getters remain unchanged.
§Setters can accept impl Into for ergonomic APIs
When into
is enabled, set
and with
methods will accept impl Into<T>
instead of T
as their
parameter. This allows callers to pass any type that can be converted into the field type, making
APIs more ergonomic. For example, a String
field can accept &str
, String
, Cow<str>
, or any
other type implementing Into<String>
. This feature works seamlessly with option_set_some
- when
both are enabled, the setter accepts impl Into<T>
and wraps the result in Some()
. This feature
can be enabled at any configuration level and only affects setter methods.
§Tuple struct support
Fieldwork supports both named structs and tuple structs. For tuple structs, you must provide a
name
attribute for each field you want to generate methods for. Fields without a name
attribute
are ignored.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, set, with, without, get_mut)]
struct Color(
#[field = "red"] u8,
#[field = "green"] u8,
#[field = "blue"] u8,
);
// Usage
let color = Color(255, 128, 0)
.with_red(200)
.with_blue(100);
assert_eq!(color.red(), 200);
assert_eq!(color.green(), 128);
assert_eq!(color.blue(), 100);
§Methods
Fieldwork supports five distinct methods: get
, set
, get_mut
, with
, and without
.
§get
Borrows the field. This can also be used to return a copy of the field using the
#[field(get(copy))]
annotation on a field, or when common copy types are detected (see above).
§Example:
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get)]
struct User {
/// whether this user is an admin
admin: bool,
/// the user's name
name: String,
/// the user's age, if set
age: Option<u8>,
/// favorite color, if set
favorite_color: Option<String>
}
generates
// GENERATED
impl User {
///Returns a copy of whether this user is an admin
pub fn admin(&self) -> bool {
self.admin
}
///Borrows the user's name
pub fn name(&self) -> &str {
&*self.name
}
///Returns a copy of the user's age, if set
pub fn age(&self) -> Option<u8> {
self.age
}
///Borrows favorite color, if set
pub fn favorite_color(&self) -> Option<&str> {
self.favorite_color.as_deref()
}
}
§set
By default, set returns &mut self
for chainable setters. If you would prefer to return ()
, use
#[fieldwork(set(chain = false))]
on the struct or an individual field.
§Example
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set)]
struct User {
/// whether this user is an admin
admin: bool,
/// the user's name
name: String
}
generates:
// GENERATED
impl User {
///Sets whether this user is an admin, returning `&mut Self` for chaining
pub fn set_admin(&mut self, admin: bool) -> &mut Self {
self.admin = admin;
self
}
///Sets the user's name, returning `&mut Self` for chaining
pub fn set_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
}
§get_mut
§Example
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get_mut)]
struct User {
/// whether this user is an admin
admin: bool,
/// the user's name
name: String,
/// the user's age, if set
age: Option<u8>,
/// favorite color, if set
favorite_color: Option<String>
}
generates the following impl block
// GENERATED
impl User {
///Mutably borrow whether this user is an admin
pub fn admin_mut(&mut self) -> &mut bool {
&mut self.admin
}
///Mutably borrow the user's name
pub fn name_mut(&mut self) -> &mut str {
&mut *self.name
}
///Mutably borrow the user's age, if set
pub fn age_mut(&mut self) -> Option<&mut u8> {
self.age.as_mut()
}
///Mutably borrow favorite color, if set
pub fn favorite_color_mut(&mut self) -> Option<&mut str> {
self.favorite_color.as_deref_mut()
}
}
§with
The with
method provides a chainable owned setter, for situations that require returning the
struct after modification.
§Example
#[derive(fieldwork::Fieldwork)]
#[fieldwork(with)]
struct User {
/// whether this user is an admin
admin: bool,
/// the user's name
name: String,
}
// GENERATED
impl User {
///Owned chainable setter for whether this user is an admin, returning `Self`
#[must_use]
pub fn with_admin(mut self, admin: bool) -> Self {
self.admin = admin;
self
}
///Owned chainable setter for the user's name, returning `Self`
#[must_use]
pub fn with_name(mut self, name: String) -> Self {
self.name = name;
self
}
}
§without
The without
method provides “negative” operations to complement with
. When without
is enabled, it changes how with
works: instead of accepting the field’s type directly, with
becomes the “positive” operation (true for bools, Some(value) for Options) and without
becomes the “negative” operation (false for bools, None for Options).
§How with
and without
work together
// With `without` - the `with` methods change behavior
#[derive(fieldwork::Fieldwork)]
#[fieldwork(with, without)]
struct Config {
debug: bool,
name: Option<String>,
}
// GENERATED
impl Config {
#[must_use]
pub fn with_debug(mut self) -> Self {
self.debug = true;
self
}
#[must_use]
pub fn without_debug(mut self) -> Self {
self.debug = false;
self
}
#[must_use]
pub fn with_name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
#[must_use]
pub fn without_name(mut self) -> Self {
self.name = None;
self
}
}
§take
Take is only ever generated for Option fields. It calls Option::take
on the field, returning the
contained value and leaving None
in its place.
// With `without` - the `with` methods change behavior
#[derive(fieldwork::Fieldwork)]
#[fieldwork(take)]
struct Config {
name: Option<String>,
}
// GENERATED
impl Config {
pub fn take_name(&mut self) -> Option<String> {
self.name.take()
}
}
§Field type behavior
Option<T>
fields:with_field(value: T)
sets toSome(value)
,without_field()
sets toNone
bool
fields:with_field()
sets totrue
,without_field()
sets tofalse
- Other types:
without
methods are not generated,with
methods work normally
The without
behavior can be disabled per-field with #[field(without = false)]
, and the automatic Option<T>
wrapping can be disabled with #[field(option_set_some = false)]
.
§Struct Configuration
vis
Sets the visibility for all generated functions, unless
otherwise overridden. `#[fieldwork(vis = "pub")]` is the default. For `pub(crate)`, use
`#[fieldwork(vis = "pub(crate)")]`. To set private visibility, use `#[fieldwork(vis = "")]`.
where_clause
This option allows you to specify a where clause for the implementation block, such as:
#[derive(fieldwork::Fieldwork, Clone)]
#[fieldwork(get, set, get_mut, with, where_clause = "PocketContents: Precious")]
struct Hobbit<PocketContents> {
/// what the hobbit has in his pocket
pocket_contents: PocketContents,
}
// GENERATED
impl<PocketContents> Hobbit<PocketContents>
where
PocketContents: Precious,
{
///Borrows what the hobbit has in his pocket
pub fn pocket_contents(&self) -> &PocketContents {
&self.pocket_contents
}
///Mutably borrow what the hobbit has in his pocket
pub fn pocket_contents_mut(&mut self) -> &mut PocketContents {
&mut self.pocket_contents
}
///Sets what the hobbit has in his pocket, returning `&mut Self` for chaining
pub fn set_pocket_contents(&mut self, pocket_contents: PocketContents) -> &mut Self {
self.pocket_contents = pocket_contents;
self
}
///Owned chainable setter for what the hobbit has in his pocket, returning `Self`
#[must_use]
pub fn with_pocket_contents(mut self, pocket_contents: PocketContents) -> Self {
self.pocket_contents = pocket_contents;
self
}
}
option_borrow_inner
Opt out of Option detection with option_borrow_inner = false
. Instead of get
returning
Option<&T>
and get_mut
returning Option<&mut T>
, get
returns &Option<T>
and get_mut
returns &mut Option<T>
. Default behavior is for Option detection to be enabled at the struct
level.
#[derive(fieldwork::Fieldwork, Clone)]
#[fieldwork(get, get_mut, option_borrow_inner = false)]
struct User {
// the user's name
name: Option<String>
}
// GENERATED
impl User {
pub fn name(&self) -> &Option<String> {
&self.name
}
pub fn name_mut(&mut self) -> &mut Option<String> {
&mut self.name
}
}
deref
Opt out of auto-deref at the struct level with deref = false
. See the Deref section for more
information.
#[derive(fieldwork::Fieldwork, Clone)]
#[fieldwork(get, get_mut, deref = false)]
struct User {
// the user's name
name: String
}
// GENERATED
impl User {
pub fn name(&self) -> &String {
&self.name
}
pub fn name_mut(&mut self) -> &mut String {
&mut self.name
}
}
option_set_some
Enable automatic wrapping of setter values in Some()
for Option<T>
fields at the struct level
with option_set_some
or option_set_some = true
. When enabled, set
and with
methods for
Option fields will accept the inner type T
instead of Option<T>
and automatically wrap the
value. This is particularly useful for builder patterns where None
represents an unset default
value.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set, with, option_set_some)]
struct User {
/// the user's nickname, if provided
nickname: Option<String>,
}
// GENERATED
impl User {
///Sets the user's nickname, if provided, returning `&mut Self` for chaining
pub fn set_nickname(&mut self, nickname: String) -> &mut Self {
self.nickname = Some(nickname);
self
}
///Owned chainable setter for the user's nickname, if provided, returning `Self`
#[must_use]
pub fn with_nickname(mut self, nickname: String) -> Self {
self.nickname = Some(nickname);
self
}
}
into
Enable impl Into<T>
parameters for setter methods at the struct level with into
or into = true
. When enabled, set
and with
methods will accept impl Into<T>
instead of T
, allowing
callers to pass any type that can be converted into the field type.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set, with, into)]
struct User {
/// the user's name
name: String,
/// the user's home directory
home_dir: PathBuf,
}
// GENERATED
impl User {
///Sets the user's name, returning `&mut Self` for chaining
pub fn set_name(&mut self, name: impl Into<String>) -> &mut Self {
self.name = name.into();
self
}
///Owned chainable setter for the user's name, returning `Self`
#[must_use]
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
///Sets the user's home directory, returning `&mut Self` for chaining
pub fn set_home_dir(&mut self, home_dir: impl Into<PathBuf>) -> &mut Self {
self.home_dir = home_dir.into();
self
}
///Owned chainable setter for the user's home directory, returning `Self`
#[must_use]
pub fn with_home_dir(mut self, home_dir: impl Into<PathBuf>) -> Self {
self.home_dir = home_dir.into();
self
}
}
rename_predicates
Rename all bool-returning methods to is_{}
at the struct level with rename_predicates
or
rename_predicates = true
.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, get_mut, rename_predicates)]
struct User {
admin: bool
}
// GENERATED
impl User {
pub fn is_admin(&self) -> bool {
self.admin
}
pub fn admin_mut(&mut self) -> &mut bool {
&mut self.admin
}
}
§Struct Method Configuration
vis
Override the struct-level definition for a specific method. #[vis = "pub(crate)", get(vis = "pub"), set, get_mut]
uses pub(crate)
for all methods other than get, which uses “pub”.
doc_template
Override the default documentation template for the specific method. Let’s say we want our documentation to say “assigns” instead of “sets”:
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set(doc_template = "Assigns {}"))]
struct User {
/// whether this user is an admin
admin: bool,
/// the user's name
name: String,
}
// GENERATED
impl User {
///Assigns whether this user is an admin
pub fn set_admin(&mut self, admin: bool) -> &mut Self {
self.admin = admin;
self
}
///Assigns the user's name
pub fn set_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
}
template
Override the method naming for all generated functions of this type. Let’s say we want our set
signature to be assign_admin
instead of set_admin
and assign_name
:
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set(template = "assign_{}"))]
struct User {
/// whether this user is an admin
admin: bool,
/// the user's name
name: String,
}
// GENERATED
impl User {
///Sets whether this user is an admin, returning `&mut Self` for chaining
pub fn assign_admin(&mut self, admin: bool) -> &mut Self {
self.admin = admin;
self
}
///Sets the user's name, returning `&mut Self` for chaining
pub fn assign_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
}
chain
(set
only)
As discussed in the Set section above, set returns &mut Self
by default. To disable this,
specify chain = false
:
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set(chain = false))]
struct User {
/// whether this user is an admin
admin: bool,
/// the user's name
name: String,
}
// GENERATED
impl User {
///Sets whether this user is an admin
pub fn set_admin(&mut self, admin: bool) {
self.admin = admin;
}
///Sets the user's name
pub fn set_name(&mut self, name: String) {
self.name = name;
}
}
option_borrow_inner
Opt out of Option detection with option_borrow_inner = false
, or if it has been opted out at the
struct level, opt back in with option_borrow_inner
or option_borrow_inner = true
for a single
method, as in get(option_borrow_inner)
or get_mut(option_borrow_inner = true)
. See
option_borrow_inner above for more information.
deref
Opt out of auto-deref at the struct method level with deref = false
. See the Deref section for more
information.
#[derive(fieldwork::Fieldwork, Clone)]
#[fieldwork(get, get_mut(deref = false))]
struct User {
// the user's name
name: String
}
// GENERATED
impl User {
pub fn name(&self) -> &str {
&*self.name
}
pub fn name_mut(&mut self) -> &mut String {
&mut self.name
}
}
option_set_some
(set
and with
only)
Enable automatic wrapping of setter values in Some()
for Option<T>
fields for a specific
method. This can be used to enable the feature for just set
or just with
, or to override the
struct-level setting.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set(option_set_some), with)]
struct User {
/// the user's nickname, if provided
nickname: Option<String>,
}
// GENERATED
impl User {
///Sets the user's nickname, if provided, returning `&mut Self` for chaining
pub fn set_nickname(&mut self, nickname: String) -> &mut Self {
self.nickname = Some(nickname);
self
}
///Owned chainable setter for the user's nickname, if provided, returning `Self`
#[must_use]
pub fn with_nickname(mut self, nickname: Option<String>) -> Self {
self.nickname = nickname;
self
}
}
into
(set
and with
only)
Enable impl Into<T>
parameters for setter methods for a specific method. This can be used to
enable the feature for just set
or just with
, or to override the struct-level setting.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set(into), with)]
struct User {
/// the user's name
name: String,
}
// GENERATED
impl User {
///Sets the user's name, returning `&mut Self` for chaining
pub fn set_name(&mut self, name: impl Into<String>) -> &mut Self {
self.name = name.into();
self
}
///Owned chainable setter for the user's name, returning `Self`
#[must_use]
pub fn with_name(mut self, name: String) -> Self {
self.name = name;
self
}
}
copy
(get
only)
By default, common Copy types such as bool will be returned by copy instead of by reference. To opt
out of this behavior for a whole struct, use #[fieldwork(get(copy = false))]
.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get(copy = false))]
struct Collection {
/// length
len: usize,
/// enabled
enabled: bool,
}
// GENERATED
impl Collection {
///Borrows length
pub fn len(&self) -> &usize {
&self.len
}
///Borrows enabled
pub fn enabled(&self) -> &bool {
&self.enabled
}
}
§Field Configuration
skip
Omit this field from all generated functions. As a shorthand, you can also use #[field = false]
.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, set)]
struct User {
/// whether this user is an admin
admin: bool,
/// the user's name
name: String,
#[field(skip)]
private: (),
#[field = false]
other_private: ()
}
// GENERATED
impl User {
///Returns a copy of whether this user is an admin
pub fn admin(&self) -> bool {
self.admin
}
///Sets whether this user is an admin, returning `&mut Self` for chaining
pub fn set_admin(&mut self, admin: bool) -> &mut Self {
self.admin = admin;
self
}
///Borrows the user's name
pub fn name(&self) -> &str {
&*self.name
}
///Sets the user's name, returning `&mut Self` for chaining
pub fn set_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
}
name
Change the name of this field for all generated methods. There are two ways to accomplish this. If
you don’t need to specify any other configuration on the field, you can use #[field = "new_name"]
,
or use #[field(name = new_name)]
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, set)]
struct User {
#[field(name = admin)]
/// whether this user is an admin
superadmin: bool,
#[field = "full_name"]
name: String,
}
// GENERATED
impl User {
///Returns a copy of whether this user is an admin
pub fn admin(&self) -> bool {
self.superadmin
}
///Sets whether this user is an admin, returning `&mut Self` for chaining
pub fn set_admin(&mut self, admin: bool) -> &mut Self {
self.superadmin = admin;
self
}
pub fn full_name(&self) -> &str {
&*self.name
}
pub fn set_full_name(&mut self, full_name: String) -> &mut Self {
self.name = full_name;
self
}
}
argument
Change the name of the argument for with
and set
. This is occasionally important for rustdocs
and lsp.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(with, set)]
struct User {
#[field(argument = is_admin)]
/// whether this user is an admin
admin: bool,
}
// GENERATED
impl User {
///Sets whether this user is an admin, returning `&mut Self` for chaining
pub fn set_admin(&mut self, is_admin: bool) -> &mut Self {
self.admin = is_admin;
self
}
///Owned chainable setter for whether this user is an admin, returning `Self`
#[must_use]
pub fn with_admin(mut self, is_admin: bool) -> Self {
self.admin = is_admin;
self
}
}
vis
Change the visibility for all generated methods for a specific field
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, set)]
struct User {
/// whether this user is an admin
admin: bool,
#[field(vis = "pub(crate)")]
/// the user's name
name: String,
}
// GENERATED
impl User {
///Returns a copy of whether this user is an admin
pub fn admin(&self) -> bool {
self.admin
}
///Sets whether this user is an admin, returning `&mut Self` for chaining
pub fn set_admin(&mut self, admin: bool) -> &mut Self {
self.admin = admin;
self
}
///Borrows the user's name
pub(crate) fn name(&self) -> &str {
&*self.name
}
///Sets the user's name, returning `&mut Self` for chaining
pub(crate) fn set_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
}
deref
If set to true
, this opts the field into deref detection for common types if the struct or struct-method have turned deref = false
.
If set to false
, this opts the specific field out of deref detection for common types, borrowing the owned type.
If set to a specific type, dereference to the specific type. Some types such as [u8]
will require
quoting.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, get_mut)]
struct User {
/// the user's name
name: String,
/// a small image in jpg format
#[field(deref = "[u8]")]
profile_thumbnail: Vec<u8>,
// opt out of deref detection so we can use the arc directly
#[field(deref = false)]
an_arc: Arc<()>,
}
// GENERATED
impl User {
///Borrows the user's name
pub fn name(&self) -> &str {
&*self.name
}
///Mutably borrow the user's name
pub fn name_mut(&mut self) -> &mut str {
&mut *self.name
}
///Borrows a small image in jpg format
pub fn profile_thumbnail(&self) -> &[u8] {
&*self.profile_thumbnail
}
///Mutably borrow a small image in jpg format
pub fn profile_thumbnail_mut(&mut self) -> &mut [u8] {
&mut *self.profile_thumbnail
}
pub fn an_arc(&self) -> &Arc<()> {
&self.an_arc
}
pub fn an_arc_mut(&mut self) -> &mut Arc<()> {
&mut self.an_arc
}
}
option_set_some
Enable or disable automatic wrapping of setter values in Some()
for this specific Option field
with option_set_some = true
or option_set_some = false
. This allows you to override struct or
struct-method level settings for individual fields.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set, with)]
struct User {
/// Nickname - uses regular Option setter
#[field(option_set_some = false)]
nickname: Option<String>,
/// Display name - uses automatic Some() wrapping
#[field(option_set_some = true)]
display_name: Option<String>,
}
// GENERATED
impl User {
///Sets Nickname - uses regular Option setter, returning `&mut Self` for chaining
pub fn set_nickname(&mut self, nickname: Option<String>) -> &mut Self {
self.nickname = nickname;
self
}
///Owned chainable setter for Nickname - uses regular Option setter, returning `Self`
#[must_use]
pub fn with_nickname(mut self, nickname: Option<String>) -> Self {
self.nickname = nickname;
self
}
///Sets Display name - uses automatic Some() wrapping, returning `&mut Self` for chaining
pub fn set_display_name(&mut self, display_name: String) -> &mut Self {
self.display_name = Some(display_name);
self
}
///Owned chainable setter for Display name - uses automatic Some() wrapping, returning `Self`
#[must_use]
pub fn with_display_name(mut self, display_name: String) -> Self {
self.display_name = Some(display_name);
self
}
}
into
Enable or disable impl Into<T>
parameters for setter methods for this specific field with
into
/into = true
or into = false
. This allows you to override struct or struct-method level
settings for individual fields.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set, with)]
struct User {
name: String,
/// Display name - uses impl Into<String> parameter
#[field(into)]
display_name: String,
}
// GENERATED
impl User {
pub fn set_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
#[must_use]
pub fn with_name(mut self, name: String) -> Self {
self.name = name;
self
}
///Sets Display name - uses impl Into<String> parameter, returning `&mut Self` for chaining
pub fn set_display_name(&mut self, display_name: impl Into<String>) -> &mut Self {
self.display_name = display_name.into();
self
}
///Owned chainable setter for Display name - uses impl Into<String> parameter, returning `Self`
#[must_use]
pub fn with_display_name(mut self, display_name: impl Into<String>) -> Self {
self.display_name = display_name.into();
self
}
}
option_borrow_inner
Opt out of Option detection for this field with option_borrow_inner = false
, or if it has been
opted out at the struct or struct method level, opt back in with option_borrow_inner
or
option_borrow_inner = true
for a single field, as in #[field(option_borrow_inner)]
or
#[field(option_borrow_inner = true)]
. See option_borrow_inner above for more
information.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, get_mut)]
struct User {
profile_thumbnail: Option<Vec<u8>>,
#[field(option_borrow_inner = false)] // opt out of option_borrow_inner
nickname: Option<String>,
}
// GENERATED
impl User {
pub fn profile_thumbnail(&self) -> Option<&[u8]> {
self.profile_thumbnail.as_deref()
}
pub fn profile_thumbnail_mut(&mut self) -> Option<&mut [u8]> {
self.profile_thumbnail.as_deref_mut()
}
pub fn nickname(&self) -> &Option<String> {
&self.nickname
}
pub fn nickname_mut(&mut self) -> &mut Option<String> {
&mut self.nickname
}
}
§Field Method Configuration
name
Specify the full function name for this particular method. Note that this overrides both template
and field-level name
.
#[derive(fieldwork::Fieldwork)]
struct User {
/// whether this user is an admin
#[field(get(name = is_an_admin))]
admin: bool,
}
// GENERATED
impl User {
///Returns a copy of whether this user is an admin
pub fn is_an_admin(&self) -> bool {
self.admin
}
}
If there are no other configuration options needed, this can be provided with the following shortcut:
#[derive(fieldwork::Fieldwork)]
struct User {
/// whether this user is an admin
#[field(get = is_an_admin)]
admin: bool,
}
// GENERATED
impl User {
///Returns a copy of whether this user is an admin
pub fn is_an_admin(&self) -> bool {
self.admin
}
}
argument
Specify the name of the argument for this specific method and field.
#[derive(fieldwork::Fieldwork)]
struct User {
/// whether this user is an admin
#[field(set(argument = is_an_admin))]
admin: bool,
}
// GENERATED
impl User {
///Sets whether this user is an admin, returning `&mut Self` for chaining
pub fn set_admin(&mut self, is_an_admin: bool) -> &mut Self {
self.admin = is_an_admin;
self
}
}
doc
Override the documentation for this specific method and field.
#[derive(fieldwork::Fieldwork)]
struct User {
#[field(set(doc = "Specify whether this user can administer this system"))]
admin: bool,
}
// GENERATED
impl User {
///Specify whether this user can administer this system
pub fn set_admin(&mut self, admin: bool) -> &mut Self {
self.admin = admin;
self
}
}
chain
(set
only)
To return ()
from this specific set
method instead of &mut Self
, provide chain = false
.
#[derive(fieldwork::Fieldwork)]
struct User {
/// whether this user is an admin
#[field(set(chain = false))]
admin: bool,
}
// GENERATED
impl User {
///Sets whether this user is an admin
pub fn set_admin(&mut self, admin: bool) {
self.admin = admin;
}
}
copy
(get
only)
Sometimes it is more useful to return a Copy
of the returned type instead of a borrow. To opt into
this behavior for a specific field, use #[field(get(copy))]
. To opt out of default copy
behavior for common types such as bool
, use #[field(get(copy = false))]
.
#[derive(fieldwork::Fieldwork)]
struct Collection {
/// length
#[field(get(copy))]
len: usize,
}
// GENERATED
impl Collection {
///Returns a copy of length
pub fn len(&self) -> usize {
self.len
}
}
skip
Omit this field from the particular method. As a shorthand, you can also use method = false
(e.g., without = false
) which is equivalent to without(skip)
.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, set)]
struct User {
/// whether this user is an admin
#[field(set(skip))]
admin: bool,
/// the user's name
name: String,
}
// GENERATED
impl User {
///Returns a copy of whether this user is an admin
pub fn admin(&self) -> bool {
self.admin
}
///Borrows the user's name
pub fn name(&self) -> &str {
&*self.name
}
///Sets the user's name, returning `&mut Self` for chaining
pub fn set_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
}
deref
For get
and get_mut
, return this derefenced type for this specific method and field. Some types
such as [u8]
will require quoting. This can also be set to true or false to opt in or out of deref
detection for common types.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(deref = false)]
struct User {
/// the user's name
#[field(get(deref = str), set, get_mut)]
name: String,
/// a small image in jpg format
#[field(get_mut(deref = true), get, set)]
profile_thumbnail: Vec<u8>,
}
// GENERATED
impl User {
///Borrows the user's name
pub fn name(&self) -> &str {
&*self.name
}
///Mutably borrow the user's name
pub fn name_mut(&mut self) -> &mut String {
&mut self.name
}
///Sets the user's name, returning `&mut Self` for chaining
pub fn set_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
///Borrows a small image in jpg format
pub fn profile_thumbnail(&self) -> &Vec<u8> {
&self.profile_thumbnail
}
///Mutably borrow a small image in jpg format
pub fn profile_thumbnail_mut(&mut self) -> &mut [u8] {
&mut *self.profile_thumbnail
}
///Sets a small image in jpg format, returning `&mut Self` for chaining
pub fn set_profile_thumbnail(&mut self, profile_thumbnail: Vec<u8>) -> &mut Self {
self.profile_thumbnail = profile_thumbnail;
self
}
}
option_set_some
(set
and
with
only)
Enable or disable automatic wrapping of setter values in Some()
for a specific method and
field. This provides the most granular control, allowing you to enable the feature for just the
set
method but not with
, or vice versa.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set, with)]
struct User {
/// Enable automatic Some() wrapping only for the set method
#[field(set(option_set_some = true))]
nickname: Option<String>,
}
// GENERATED
impl User {
///Sets Enable automatic Some() wrapping only for the set method, returning `&mut Self` for chaining
pub fn set_nickname(&mut self, nickname: String) -> &mut Self {
self.nickname = Some(nickname);
self
}
///Owned chainable setter for Enable automatic Some() wrapping only for the set method, returning `Self`
#[must_use]
pub fn with_nickname(mut self, nickname: Option<String>) -> Self {
self.nickname = nickname;
self
}
}
into
(set
and with
only)
Enable or disable impl Into<T>
parameters for a specific method and field. This provides the most
granular control, allowing you to enable the feature for just the set
method but not with
, or
vice versa.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(set, with)]
struct User {
/// Enable impl Into<String> only for the set method
#[field(set(into))]
name: String,
}
// GENERATED
impl User {
///Sets Enable impl Into<String> only for the set method, returning `&mut Self` for chaining
pub fn set_name(&mut self, name: impl Into<String>) -> &mut Self {
self.name = name.into();
self
}
///Owned chainable setter for Enable impl Into<String> only for the set method, returning `Self`
#[must_use]
pub fn with_name(mut self, name: String) -> Self {
self.name = name;
self
}
}
option_borrow_inner
Opt out of Option detection for this field and method with #[field(option_borrow_inner = false)]
, or if it has been opted out at the struct, struct method, or field level, opt back in with
option_borrow_inner
or option_borrow_inner = true
for a single field and method, as in
#[field(get(option_borrow_inner))]
or #[field(get_mut(option_borrow_inner = true))]
. See
option_borrow_inner above for more information.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(get, get_mut)]
struct User {
profile_thumbnail: Option<Vec<u8>>,
// opt out of option_borrow_inner just for get_mut, so we can use Option::insert or similar
#[field(get_mut(option_borrow_inner = false))]
nickname: Option<String>,
}
// GENERATED
impl User {
pub fn profile_thumbnail(&self) -> Option<&[u8]> {
self.profile_thumbnail.as_deref()
}
pub fn profile_thumbnail_mut(&mut self) -> Option<&mut [u8]> {
self.profile_thumbnail.as_deref_mut()
}
pub fn nickname(&self) -> Option<&str> {
self.nickname.as_deref()
}
pub fn nickname_mut(&mut self) -> &mut Option<String> {
&mut self.nickname
}
}
§How fieldwork selects which methods to generate for which fields
In order to be maximally expressive, fieldwork can operate in both opt-in and opt-out
mode. #[derive(fieldwork::Fieldwork)]
does nothing without at least one #[fieldwork]
/#[field]
attribute.
§Opt-out
If a #[fieldwork(get, set, with, without, get_mut)]
attribute is applied to the struct, it applies those
methods to all fields that don’t have #[field(skip)]
(to skip the entire field) or, using get
as an example, #[field(get(skip)]
or #[field(get = false)]
to skip just the get method for the particular field.
§Opt-in
It is also possible to omit the struct-level attribute and opt individual fields in with eg
#[field(get, set)]
.
If you need to specify struct-level configuration in order to reduce repetition but still want to
operate in an opt-in mode instead of using skip
, fieldwork supports opt_in
as a top level
argument. It is also possible to specify opt_in
at a field level, which will only include the
methods specified on that field.
#[derive(fieldwork::Fieldwork)]
#[fieldwork(opt_in, get(template = "get_{}"))]
struct User {
/// whether this user is an admin
#[field = true]
admin: bool,
/// the user's name
#[field(set)]
name: String,
private: ()
}
// GENERATED
impl User {
///Returns a copy of whether this user is an admin
pub fn get_admin(&self) -> bool {
self.admin
}
///Sets the user's name, returning `&mut Self` for chaining
pub fn set_name(&mut self, name: String) -> &mut Self {
self.name = name;
self
}
}
§License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Derive Macros§
- Fieldwork
- see crate-level documentation