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

Make library always no-std compliant #7

Merged
merged 1 commit into from
May 30, 2024
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
9 changes: 8 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
[package]
name = "derive-ctor"
version = "0.2.1"
version = "0.2.2"
description = "Adds `#[derive(ctor)]` which allows for the auto-generation of a constructor."
keywords = ["derive", "macro", "trait", "procedural", "no_std"]
authors = ["Evan Cowin"]
license = "MIT"
repository = "https://github.com/ImaMapleTree/derive-ctor"
edition = "2021"
exclude = [".github/*", ".gitignore"]
categories = ["no-std", "rust-patterns"]

[lib]
proc-macro = true

[features]
no-std = []

[dependencies]
syn = { version = "2.0.*" }
quote = { version = "1.*" }
proc-macro2 = { version = "1.0.*" }
proc-macro2 = { version = "1.0.*" }
heck = { version = "0.5.*" }
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# derive-ctor

`derive-ctor` is a Rust procedural macro crate that allows you to easily generate constructor methods for your structs. With the `#[derive(ctor)]` attribute, you can automatically create a constructor(s) for structs and enums. The crate also provides various options to customize the generated constructor methods.
`derive-ctor` is a Rust procedural macro crate that allows you to easily generate constructor methods for your structs.
With the `#[derive(ctor)]` attribute, you can automatically create a constructor(s) for structs and enums. The crate also
provides various options to customize the generated constructor methods.

## Features

- Automatically generate a constructor method for structs and enums with `#[derive(ctor)]`.
- Customize the name and visibility of the auto-generated constructor using `#[ctor(visibility method_name)]`.
- Supports const constructors by adding the "const" keyword.
Expand All @@ -16,20 +17,15 @@
- Use **expr(TYPE -> EXPRESSION)** to add a parameter with the specified type, which will be used to generate the final field value.
- **into** - Change the parameter type for the generated method to `impl Into<Type>`.
- **iter(FROM_TYPE)** - Change the parameter type for the generated method to `impl IntoIterator<Item=FROM_TYPE>`.
- Support no-std via `features = ["no-std"]`
- No reliance on the standard library (no-std out of the box).

## Basic Usage

Add `derive-ctor` to your `Cargo.toml`:

```toml
[dependencies]
derive-ctor = "0.2.1"
```

Import the crate in your Rust code:
```rust
use derive_ctor::ctor;
derive-ctor = "0.2.2"
```

Annotate your struct with `#[derive(ctor)]` to automatically generate a `new` constructor:
Expand All @@ -48,7 +44,7 @@ let my_struct = MyStruct::new(1, String::from("Foo"));

## Struct Configurations

### Visibiltiy and Construtor Name
### Visibility and Constructor Name

You can modify the name and visibility of the generated method, and define additional
constructors by using the `#[ctor]` attribute on the target struct after `ctor` is derived.
Expand Down Expand Up @@ -91,7 +87,7 @@ let default: MyStruct = Default::default();

## Enum Configurations

By default a constructor will be generated for each variant. This constructor by default will match the name of its
By default, a constructor will be generated for each variant. This constructor by default will match the name of its
respective variant and will be public. This default behaviour can be changed by annotating the enum with
`#[ctor(prefix = PREFIX, visibility = VISIBILITY)]`. Note that both parameters are optional within the attribute.
Specifying this attribute will change the **default** generated method for each variant, however, each variant
Expand Down
44 changes: 11 additions & 33 deletions src/enums.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use proc_macro::TokenStream;

#[cfg(feature = "no-std")]
use alloc::string::String;
#[cfg(feature = "no-std")]
use alloc::vec;
#[cfg(feature = "no-std")]
use alloc::string::ToString;
#[cfg(feature = "no-std")]
use alloc::vec::Vec;
use heck::ToSnakeCase;

use proc_macro2::Span;
use quote::quote;
Expand Down Expand Up @@ -175,34 +171,16 @@ fn create_ctor_enum_impl(
}

fn convert_to_snakecase(method_ident: Ident) -> Result<Ident, Error> {
let mut snake_case = String::new();
let mut was_lower = false;

let ident_string = method_ident.to_string();

let mut ident_chars = ident_string.chars();
loop {
let c = match ident_chars.next() {
Some(c) if c == '_' => {
snake_case.push(c.to_ascii_lowercase());
was_lower = false;
continue;
},
Some(c) => c,
None => break,
};

let lower_or_numeric = c.is_ascii_lowercase() || c.is_numeric();

if was_lower && !lower_or_numeric {
snake_case.push('_')
}

snake_case.push(c.to_ascii_lowercase());

was_lower = lower_or_numeric;
}

let trimmed_start_str = ident_string.trim_start_matches('_');
let trimmed_start_end_str = trimmed_start_str.trim_end_matches('_');

let leading_underscore_count = ident_string.len() - trimmed_start_str.len();
let trailing_underscore_count = trimmed_start_str.len() - trimmed_start_end_str.len();

let snake_case = "_".repeat(leading_underscore_count)
+ &ident_string.to_snake_case() + &"_".repeat(trailing_underscore_count);

syn::parse_str(&snake_case)
}

Expand All @@ -213,7 +191,7 @@ fn test_convert_to_snakecase() {
assert_eq!(convert_to_snakecase(Ident::new("Test1", Span::mixed_site())).unwrap(), Ident::new("test1", Span::mixed_site()));
assert_eq!(convert_to_snakecase(Ident::new("ONETWO", Span::mixed_site())).unwrap(), Ident::new("onetwo", Span::mixed_site()));
assert_eq!(convert_to_snakecase(Ident::new("OneTwo", Span::mixed_site())).unwrap(), Ident::new("one_two", Span::mixed_site()));
assert_eq!(convert_to_snakecase(Ident::new("_Abc", Span::mixed_site())).unwrap(), Ident::new("_abc", Span::mixed_site()));
assert_eq!(convert_to_snakecase(Ident::new("__Abc__", Span::mixed_site())).unwrap(), Ident::new("__abc__", Span::mixed_site()));
assert_eq!(convert_to_snakecase(Ident::new("A_B", Span::mixed_site())).unwrap(), Ident::new("a_b", Span::mixed_site()));
assert_eq!(convert_to_snakecase(Ident::new("A_b", Span::mixed_site())).unwrap(), Ident::new("a_b", Span::mixed_site()));
assert_eq!(convert_to_snakecase(Ident::new("abCdEf", Span::mixed_site())).unwrap(), Ident::new("ab_cd_ef", Span::mixed_site()));
Expand Down
6 changes: 0 additions & 6 deletions src/fields.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
#[cfg(feature = "no-std")]
extern crate alloc;
#[cfg(feature = "no-std")]
use alloc::collections::BTreeSet as HashSet;
#[cfg(feature = "no-std")]
use alloc::string::ToString;

#[cfg(not(feature = "no-std"))]
use std::collections::HashSet;


use proc_macro2::{Delimiter, Punct, Span};
use proc_macro2::Spacing::Alone;
Expand Down
5 changes: 1 addition & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
#![cfg_attr(feature = "no-std", no_std, doc = "Removes all std library dependencies within library.")]
#![no_std]
#![doc = include_str!("../README.md")]

#[cfg(feature = "no-std")]
extern crate alloc;
#[cfg(feature = "no-std")]
use alloc::format;
#[cfg(feature = "no-std")]
use alloc::string::ToString;


Expand Down
12 changes: 0 additions & 12 deletions src/structs.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
#[cfg(feature = "no-std")]
extern crate alloc;
#[cfg(feature = "no-std")]
use alloc::vec;
#[cfg(feature = "no-std")]
use alloc::vec::Vec;
#[cfg(feature = "no-std")]
use alloc::collections::BTreeSet as HashSet;
#[cfg(feature = "no-std")]
use alloc::string::ToString;

#[cfg(not(feature = "no-std"))]
use std::collections::HashSet;
#[cfg(not(feature = "no-std"))]
use std::vec;
#[cfg(not(feature = "no-std"))]
use std::vec::Vec;

use proc_macro::TokenStream;

use proc_macro2::Span;
Expand Down