Skip to content

Commit

Permalink
feat(enum): set + doc and examples
Browse files Browse the repository at this point in the history
  • Loading branch information
makcandrov committed Oct 11, 2024
1 parent ba889de commit ccdae59
Show file tree
Hide file tree
Showing 28 changed files with 292 additions and 44 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "quick-impl"
version = "0.1.3"
version = "0.1.4"
edition = "2021"
repository = "https://github.com/makcandrov/quick-impl"
homepage = "https://github.com/makcandrov/quick-impl"
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# quick-impl

`quick-impl` is a Rust procedural macro that simplifies working with enums and structures by generating common methods and traits for each variant/field. This helps reduce boilerplate code and enhances the ergonomics of using enums and structures in your Rust projects.
`quick-impl` is a Rust procedural macro that simplifies working with enums and structs by generating common methods and traits for each variant or field. This helps reduce boilerplate code and enhances the ergonomics of using enums and structs in your Rust projects.

## Features

Expand All @@ -9,9 +9,10 @@
- `as_ref` - Returns an immutable reference to the associated data of the enum variant.
- `as_ref_mut` - Returns a mutable reference to the associated data of the enum variant.
- `from` - Creates an instance of the enum variant from the associated data.
- `into` - Converts the enum into the variant associated data, returning an [`Option`].
- `into` - Converts the enum into the associated data of the variant, returning an [`Option`].
- `is` - Checks if the enum variant matches a specified variant.
- `try_into` - Converts the enum into the variant associated data, returning a [`Result`].
- `set` - Replaces the current instance with a new instance of the specified variant.
- `try_into` - Converts the enum into the associated data of the variant, returning a [`Result`].

### Enums traits

Expand All @@ -23,11 +24,11 @@

- `get` - A getter for the field. Returns a reference to the field.
- `get_clone` - A getter for the field. Returns a clone of the field.
- `get_mut` - A mutable getter for the field.
- `into` - Converts the structure into the field.
- `get_mut` - A mutable getter for a field.
- `into` - Converts the struct into the field.
- `set` - A setter for the field.
- `take` - Returns the field and replaces it with its default value.
- `with` - Returns the sutrcture with the field modified.
- `with` - Returns the struct with the field modified.

### Structures traits

Expand Down Expand Up @@ -66,6 +67,7 @@ use quick_impl::QuickImpl;
enum YourEnum {
#[quick_impl(pub const is)]
Variant1,

#[quick_impl(pub as_ref, pub(crate) as_ref_mut, impl From)]
Variant2(i32),
// ... add attributes to other variants as needed
Expand All @@ -82,6 +84,6 @@ fn main() {
}
```

More examples can be found in [examples].
More examples can be found in the [examples].

[examples]: https://github.com/makcandrov/quick-impl/tree/main/examples
41 changes: 41 additions & 0 deletions examples/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use quick_impl::QuickImpl;

#[derive(QuickImpl, Default)]
struct Config {
#[quick_impl(pub get, pub set, take, pub with)]
host: String,

#[quick_impl(pub get, pub set, take, pub with)]
port: u16,

#[quick_impl(pub get, pub set, take, pub with)]
max_connections: usize,
}

fn main() {
// Using the `Default` trait to create a Config instance
let mut config = Config::default();

// Setting values
config.set_host("localhost".to_string());
config.set_port(8080);
config.set_max_connections(100);

assert_eq!(config.get_host(), "localhost");
assert_eq!(*config.get_port(), 8080);
assert_eq!(*config.get_max_connections(), 100);

// Using `take` to retrieve and reset fields
let host = config.take_host();
assert_eq!(host, "localhost");
assert_eq!(config.get_host(), ""); // After `take`, it should be default (empty String)

let port = config.take_port();
assert_eq!(port, 8080);
assert_eq!(*config.get_port(), 0); // After `take`, it should be default (0)

// Using `with` to modify fields fluently
let new_config = config.with_host("127.0.0.1".to_string()).with_port(3000);
assert_eq!(new_config.get_host(), "127.0.0.1");
assert_eq!(*new_config.get_port(), 3000);
}
1 change: 1 addition & 0 deletions examples/person.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use quick_impl::QuickImpl;
struct Person {
#[quick_impl(pub get)]
name: String,

#[quick_impl(pub get, pub set, get_mut)]
age: u32,
}
Expand Down
3 changes: 3 additions & 0 deletions examples/shapes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ use quick_impl::QuickImpl;
pub enum Shape {
#[quick_impl(pub const is)]
Circle(f64),

#[quick_impl(pub as_ref, as_ref_mut, impl From)]
Rectangle(f64, f64),

#[quick_impl(pub as_ref, as_ref_mut, pub into)]
Square(f64),

#[quick_impl(pub const from = "create_cuboid", pub const is, pub into)]
Cuboid { width: f64, height: f64, depth: f64 },
}
Expand Down
47 changes: 47 additions & 0 deletions examples/status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use quick_impl::QuickImpl;

#[derive(Debug, QuickImpl)]
pub enum Status {
#[quick_impl(pub const is)]
Pending,

#[quick_impl(pub const is, pub try_into, impl TryInto)]
Approved(i32),

#[quick_impl(pub const is, pub try_into, impl TryInto)]
Rejected(String),
}

#[derive(QuickImpl)]
pub struct Wrapper {
#[quick_impl(impl Deref, impl DerefMut)]
data: Vec<u8>,
}

fn main() {
// Enum example with `try_into` and `is`
let status = Status::Approved(200);
assert!(status.is_approved());

// Use `try_into` to convert to the associated data
let approved_code: Result<i32, Status> = status.try_into_approved();
assert_eq!(approved_code.unwrap(), 200);

let rejected_status = Status::Rejected("Invalid request".to_string());
assert!(rejected_status.is_rejected());

let rejection_reason: Result<String, Status> = rejected_status.try_into();
assert_eq!(rejection_reason.unwrap(), "Invalid request");

// Struct example with `Deref` and `DerefMut`
let mut wrapper = Wrapper {
data: vec![1, 2, 3],
};

// Accessing inner `Vec<u8>` through Deref
assert_eq!(wrapper.len(), 3); // Calls `Vec<u8>::len`

// Mutating inner `Vec<u8>` through DerefMut
wrapper.push(4);
assert_eq!(wrapper.len(), 4); // Now has 4 elements
}
4 changes: 4 additions & 0 deletions examples/web_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ use quick_impl::QuickImpl;
pub enum WebEvent {
#[quick_impl(pub const is)]
PageLoad,

#[quick_impl(pub const is)]
PageUnload,

#[quick_impl(pub as_ref, pub(crate) as_ref_mut, impl From)]
KeyPress(char),

#[quick_impl(pub as_ref, pub(crate) as_ref_mut, pub into)]
Paste(String),

#[quick_impl(pub from = "click_from_coordinates", pub const is, pub as_ref)]
Click { x: i64, y: i64 },
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/enums/methods/as_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::tokens::{destructure_data, destructure_types, get_delimiter};
build_enum_name! { ConfigName, "as_{}" }
build_enum_doc! {
ConfigDoc,
"Returns an immutable reference to the associated data if it is the [`{}::{}`] variant. Otherwise, returns `None`.",
"Returns an immutable reference to the associated data if the variant is [`{}::{}`]. Otherwise, returns `None`.",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/enums/methods/as_ref_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::tokens::{destructure_data, destructure_types, get_delimiter};
build_enum_name! { ConfigName, "as_{}_mut" }
build_enum_doc! {
ConfigDoc,
"Returns a mutable reference to the associated data if it is the [`{}::{}`] variant. Otherwise, returns `None`.",
"Returns a mutable reference to the associated data if the variant is [`{}::{}`]. Otherwise, returns `None`.",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/enums/methods/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::tokens::{destructure_data, destructure_data_with_types, get_delimiter
build_enum_name! { ConfigName, "from_{}" }
build_enum_doc! {
ConfigDoc,
"Creates a [`{}::{}`] variant from the associated data.",
"Creates a [`{}::{}`] variant from the provided data.",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/enums/methods/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::tokens::{destructure_data, destructure_types, get_delimiter};
build_enum_name! { ConfigName, "into_{}" }
build_enum_doc! {
ConfigDoc,
"Converts into the associated data if it is the [`{}::{}`] variant. Otherwise, returns `None`.",
"onverts to the associated data if the variant is [`{}::{}`]. Otherwise, returns `None`.",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/enums/methods/is.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::expand::Context;
build_enum_name! { ConfigName, "is_{}" }
build_enum_doc! {
ConfigDoc,
"Returns `true` if it is the [`{}::{}`] variant. Otherwise, returns `false`.",
"Returns `true` if the variant is [`{}::{}`]. Otherwise, returns `false`.",
}

build_config! {
Expand Down
3 changes: 3 additions & 0 deletions src/components/enums/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ pub use into::enum_method_into;
mod is;
pub use is::enum_method_is;

mod set;
pub use set::enum_method_set;

mod try_into;
pub use try_into::enum_method_try_into;
47 changes: 47 additions & 0 deletions src/components/enums/methods/set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use proc_macro2::{Delimiter, TokenStream};
use quote::quote;
use syn::Variant;

use crate::attributes::{Attribute, MethodAttribute};
use crate::config::{build_config, build_enum_doc, build_enum_name};
use crate::expand::Context;
use crate::tokens::{destructure_data, destructure_data_with_types, get_delimiter};

build_enum_name! { ConfigName, "set_{}" }
build_enum_doc! {
ConfigDoc,
"Replaces the current instance with a new instance of the [`{}::{}`] variant, returning the original instance.",
}

build_config! {
Config,
(name, ConfigName, true),
(doc, ConfigDoc, false),
}

pub fn enum_method_set(
context: &Context,
variant: &Variant,
attribute: &Attribute,
method_attr: &MethodAttribute,
) -> syn::Result<TokenStream> {
let config = Config::new(context, attribute, variant)?;

let fields = &variant.fields;
let delimiter = get_delimiter(fields);

let input = destructure_data_with_types(fields, quote! {}, Delimiter::None, false);
let destruct = destructure_data(fields, quote! {}, quote! {}, delimiter, true);

let variant_ident = &variant.ident;
let keywords = method_attr.keywords();
let doc = config.doc;
let method_ident = config.name;

Ok(quote! {
#[doc = #doc]
#keywords fn #method_ident(&mut self, #input) -> Self {
::core::mem::replace(self, Self:: #variant_ident #destruct )
}
})
}
2 changes: 1 addition & 1 deletion src/components/enums/methods/try_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::tokens::{destructure_data, destructure_types, get_delimiter};
build_enum_name! { ConfigName, "try_into_{}" }
build_enum_doc! {
ConfigDoc,
"Converts into the associated data if it is the [`{}::{}`] variant. Otherwise, returns `Err(self)`.",
"Converts to the associated data if the variant is [`{}::{}`]. Otherwise, returns `Err(self)`.",
}

build_config! {
Expand Down
15 changes: 5 additions & 10 deletions src/components/enums/mod.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
use methods::enum_method_try_into;
use syn::DataEnum;
use traits::enum_trait_try_into;

use self::methods::{
enum_method_as_ref, enum_method_as_ref_mut, enum_method_from, enum_method_into, enum_method_is,
};
use self::traits::{enum_trait_default, enum_trait_from};
use crate::attributes::{AttributeType, Attributes};
use crate::expand::{Context, Implems};
use crate::idents::methods::{
METHOD_AS_REF, METHOD_AS_REF_MUT, METHOD_FROM, METHOD_INTO, METHOD_IS, METHOD_TRY_INTO,
};
use crate::idents::traits::{TRAIT_DEFAULT, TRAIT_FROM, TRAIT_TRY_INTO};
use crate::idents::{methods::*, traits::*};

mod methods;
use methods::*;

mod traits;
use traits::*;

pub fn enum_impl(
context: &Context<'_>,
Expand All @@ -37,6 +31,7 @@ pub fn enum_impl(
METHOD_FROM => enum_method_from(context, &variant, attribute, method_attr)?,
METHOD_INTO => enum_method_into(context, &variant, attribute, method_attr)?,
METHOD_IS => enum_method_is(context, &variant, attribute, method_attr)?,
METHOD_SET => enum_method_set(context, &variant, attribute, method_attr)?,
METHOD_TRY_INTO => {
enum_method_try_into(context, &variant, attribute, method_attr)?
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/enums/traits/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::tokens::{destructure_data, destructure_types, get_delimiter};

build_enum_doc! {
ConfigDoc,
"Creates a [`{}::{}`] variant from the associated data.",
"Creates a [`{}::{}`] variant from the provided data.",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/enums/traits/try_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::tokens::{destructure_data, destructure_types, get_delimiter};

build_enum_doc! {
ConfigDoc,
"Converts into the associated data if it is the [`{}::{}`] variant. Otherwise, returns `Err(self)`.",
"Converts to the associated data if the variant is [`{}::{}`]. Otherwise, returns `Err(self)`.",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/structs/methods/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::tokens::IndexedField;
build_enum_name! { ConfigName, "get_{}" }
build_enum_doc! {
ConfigDoc,
"A getter for the field `{1}` of [`{0}`].",
"A getter for the `{1}` field of [`{0}`].",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/structs/methods/get_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::tokens::IndexedField;
build_enum_name! { ConfigName, "get_{}" }
build_enum_doc! {
ConfigDoc,
"A getter for the field `{1}` of [`{0}`].",
"A getter for the `{1}` field of [`{0}`].",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/structs/methods/get_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::tokens::IndexedField;
build_enum_name! { ConfigName, "get_{}_mut" }
build_enum_doc! {
ConfigDoc,
"A mutable getter for the field `{1}` of [`{0}`].",
"A mutable getter for the `{1}` field of [`{0}`].",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/structs/methods/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::tokens::IndexedField;
build_enum_name! { ConfigName, "into_{}" }
build_enum_doc! {
ConfigDoc,
"Converts into the field `{1}` of [`{0}`].",
"Converts into the `{1}` field of [`{0}`].",
}

build_config! {
Expand Down
2 changes: 1 addition & 1 deletion src/components/structs/methods/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::tokens::IndexedField;
build_enum_name! { ConfigName, "set_{}" }
build_enum_doc! {
ConfigDoc,
"A setter for the field `{1}` of [`{0}`]. Returns the old value.",
"A setter for the `{1}` field of [`{0}`]. Returns the previous value.",
}

build_config! {
Expand Down
Loading

0 comments on commit ccdae59

Please sign in to comment.