Update strum_macros to 0.26.2
Test: m
Change-Id: Ia4b992b3227b2bde5ca332adea4fded67fe0b28d
diff --git a/crates/strum_macros/.android-checksum.json b/crates/strum_macros/.android-checksum.json
new file mode 100644
index 0000000..36dc750
--- /dev/null
+++ b/crates/strum_macros/.android-checksum.json
@@ -0,0 +1 @@
+{"package":null,"files":{"src/lib.rs":"52d5f49927e5e5c3e3d6bd668c5a24599c516121df7cbd79145943cd804586d0","src/macros/enum_discriminants.rs":"d80616c8889ace6e1f1e8eb036140a7127bb0a536414a20cc984933b72aa41c0","MODULE_LICENSE_MIT":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","src/helpers/case_style.rs":"a401e30dc1198a382681dfbc7edabe641df80e6717b6b57ad3d8f9d3457c24ca","src/macros/from_repr.rs":"83b62edd8bfe91db286bd1531a77082d30d4f9d4ee8185f7a95689fa4860365f","src/macros/strings/display.rs":"20388c5ff17fe164d99988ca3557b0e4db2e54ca90ac4fd6b3bc34443ddbf9b4","src/macros/enum_iter.rs":"761d34a9633be1f920cc04910cd70e39dd2e1830fdb3a7239eb95d79e30f7a4f","TEST_MAPPING":"130934553eb809f532f064b6d924421ef21dc989325f59a50bd6f726f2cba884","LICENSE":"1884079d8260fd6643f913346f742df6dabc32a0d1d1a973f6c70b2ae20383fe","src/helpers/variant_props.rs":"c007bed549c30a6c9a64521639608e7dd3e6f9c70553ec78a183ebda02bddd54","src/macros/enum_table.rs":"8002a82697ae43cba9cbb77baf3c6aa60f810f948c7ca97d46f0a2a96e5dbdfe","src/macros/strings/mod.rs":"5f0003e2cb561686d347d008fd890d52a691e40bdbeef8a36fadc11512abcfbe","src/helpers/type_props.rs":"9f7227dfe0f9ab4f1ee4dfdcb960c49db4706497861ae82cc9a9db904287ebab","src/macros/enum_properties.rs":"c1a386c408a09b7af7754b0e28220ebae02abd75b123d0edac8a0f00880a633b","Cargo.toml":"90f40b0d04e3d330cd5fae3c5d1d04944045e3a0ba8dce9a0bd47423d29c53e0","src/helpers/metadata.rs":"abb82cefe57fc10d87c4fe5a4846eacd97ca3306851e28b7c7699fbdec412ddf","METADATA":"8f07cd6e794503efad77467157be7fa09c030036131a37cc2da4842029a20eeb","src/helpers/mod.rs":"928c2e83113fac0183b291b2e064bd6444140698e4b4f2410e55d85ea1deab28","src/macros/enum_variant_array.rs":"4a82897758e6059aae96e8d999e7c69e226bd61fe54c0d9cf610bdfd38e407b9","src/macros/enum_variant_names.rs":"9c3bac48f80b7aab75d779377de56ba143e7f831d659bb4e1070ee4e6181d2fe","src/macros/strings/to_string.rs":"ef77f2087f20dfc2775324e99e178bc40831c1a3017de1a58104b901daced823","src/macros/enum_try_as.rs":"6299527af91a6478ca48c56871c2649cb19eaa371186b08b81fc21bea76b18c6","src/macros/enum_is.rs":"a67764b68d10774516aec79b4a121aa8526b85c1ba4eb19406ea839925ac2fbf","src/macros/enum_count.rs":"c3f068684816e891171537745c5a4a86bbb76f74578e079880a874ef0d1e7fc5","src/macros/mod.rs":"d3f44f5625d76d0f114ff394d6b95723ca9f6b2a089784c09b12dab42f54cc47","src/macros/strings/from_string.rs":"913f990421d76985c4a609fc13d58f31c7b25dfc48f37c564feaf48a61e66fe0","README.md":"01b98982954529385a99d3557d04bfd9d4ff2773a87fc5a8cf5d57680fe936a2","cargo_embargo.json":"fa702ccdadce959e0baa3621834e37c34b5b20d1d2993055e3efbb9ad62a03b8","src/helpers/inner_variant_props.rs":"fb25aac1a36405f4912d239241ed50d304946b3bdff79bd90466b41fa99785cc","Android.bp":"adc2184a419c0200731d0e6a81a8d1a449083737aa53c9014586e420b74fb751","src/macros/enum_messages.rs":"ad5fc215c0e992dc09024c3ad76b8bd18a56147170bab71ab964e58c44e82cad","src/macros/strings/as_ref_str.rs":"9f7b73f0848d346f54f0efb78de66da53c29b4d22386075554eed603e4fdb93d",".cargo-checksum.json":"5c20c1ce69ea23d2d61dcf13b032ae2aa2b29fa8a34e1abf2f57e4d355ad8bc4"}}
\ No newline at end of file
diff --git a/crates/strum_macros/.cargo-checksum.json b/crates/strum_macros/.cargo-checksum.json
index a9f9601..16f7225 100644
--- a/crates/strum_macros/.cargo-checksum.json
+++ b/crates/strum_macros/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"23072aa33960a8ac715daca71a1aef3236d09e65c8b3dc1c69b1fd0ab5f83a90","LICENSE":"8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42","README.md":"78ad65413ae9989028bfef9ebf198dee8dc74af3ec7fb60d26e0d984b979c126","src/helpers/case_style.rs":"6094ea43fb51d249f0685957060bd7170e5bcea3356a93569a01ef011dc41cdb","src/helpers/metadata.rs":"87320d82b70220a75ccf411dc66a4e0b9eb959e7ad1602d82006a7cd07d6e310","src/helpers/mod.rs":"e3e8977fcf94b7cf3b769d976cc998707cc4641e5857f88859a10d2dc1bd8d5a","src/helpers/type_props.rs":"f9c5f1ae29173f5dd51025f83e723e176d47b01de0abc45105cedaa7abccbb47","src/helpers/variant_props.rs":"d528ce14015850ac1b34cbea46a8836bdaf50a290a2fe92ca560af6b192b5507","src/lib.rs":"eb2cc12a6136780ba93ab069d01fdfdaa41b3ca959ccb6f5350ec9006f778d0b","src/macros/enum_count.rs":"44084ab800ca8fca3b17f76c6025e8e59855598c0119807ce7bc80d5dc345d99","src/macros/enum_discriminants.rs":"7513c5eb515da55ff6507c8fd80fd1c626de78e68ea737d3edea48e0ee56337c","src/macros/enum_is.rs":"c56e67aa93b173b1fc0301106864ed567bc708e55bc28982ad05f5c8efa343ce","src/macros/enum_iter.rs":"81f62d61cfe50320ee4cacb09d19f817ec04c0b7c77c6a7aad24dafe093eedab","src/macros/enum_messages.rs":"d23b427e1b0f8ab9709e93837aba16bff12ccd92fb55bf013ca80a292ed7ffc5","src/macros/enum_properties.rs":"a9e1bf27504d46df689aad8910c7ad8ae7b3780ee97b6e659b2b19e481474030","src/macros/enum_try_as.rs":"b3bbc5020351251f005cfadf86a8759bb28172c9d176684b90074d32cc2ae3dd","src/macros/enum_variant_names.rs":"7d6d381ab10c603d1e87a089057138ef21c9b3de1246ba1fdcec894ef239a461","src/macros/from_repr.rs":"69b904604cdb0287f9dccafa15592627e32d3edd2310a2ddaa53a5547e903f71","src/macros/mod.rs":"13b0e3b5ddc63e4750aa29fbdb4a338307367cf148099b17813e340fe5dfdf32","src/macros/strings/as_ref_str.rs":"73ce53ae25c8a148075e858ab03eb13fe7cba350cfb649048b5b6582703fc5da","src/macros/strings/display.rs":"c53989fcb45a9d22f0990cf620e0ffff9e2d9e46a93edc780901ab43650dfb29","src/macros/strings/from_string.rs":"99d8b8c1a7aa99ab12f63e5f74e96c980e17740738e800a854d688d6fed59ecb","src/macros/strings/mod.rs":"b7937f72a46c807fd87c787f3d23029509565a0388a30106e7026363782f2b56","src/macros/strings/to_string.rs":"3d43865603be815cc52a8364bc3e969af7a1bd9532d9461fcce8252098488b0a"},"package":"23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"}
\ No newline at end of file
+{"files":{"Cargo.toml":"092265d6ab49a30549ada59bb62e244b28649758ec3a789af589b09e07c3cc48","LICENSE":"8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42","README.md":"4d1475987c8866226ac2f4136dfb5196dfd0b16a2fa3d63ddbd0f4f5011f65af","src/helpers/case_style.rs":"ac429454fad24d1a5a9829711f2024ae9477ffad7cc293b711fe588d1a5e1edf","src/helpers/inner_variant_props.rs":"09c4a2c73aadaaa934e6a20b429b99d4229ce76a4cc1edc916298100d971deb4","src/helpers/metadata.rs":"a9391071f1d4750781eda79bdf4553e77c65c28b1df87dfb3d00df6b290c18dd","src/helpers/mod.rs":"dc171dd27e723aede6c45cb981d48c710f42167afb06a5fbf013470a2962b9f2","src/helpers/type_props.rs":"cb1cdfb0e7927d3b0e364237e7048285ef5968e036d2ebacea642c66d96cc669","src/helpers/variant_props.rs":"36cc466e29b20246f87b9b87d39e4d23545596c522a201e0fb320ac2b231b045","src/lib.rs":"5198869272f00b3adc78d2ffbfb129f42062f3d5c1f3d06fe081a06c3440b51f","src/macros/enum_count.rs":"44084ab800ca8fca3b17f76c6025e8e59855598c0119807ce7bc80d5dc345d99","src/macros/enum_discriminants.rs":"98abbefad100dda4aaafa959ebb9fa77f518dd483414113e6c8bd1eea193bdff","src/macros/enum_is.rs":"cd63fb79f4f9b3479f11bce2ad45d3cb5d23f1d5c9c2a5fbb7af7e813fb70fa7","src/macros/enum_iter.rs":"a6c42eefae4e442301dfd9a622aa6a502aac7a0fc9ab7e2b8aaea221cd9d06b0","src/macros/enum_messages.rs":"ee89c3a10ebb553aa9c342f4b3b4346e97be1744fc5df72f580a331d4ea87393","src/macros/enum_properties.rs":"a9e1bf27504d46df689aad8910c7ad8ae7b3780ee97b6e659b2b19e481474030","src/macros/enum_table.rs":"434da77cbca12082d4daeba28ef0e416cea3064f1f641b31f4a330589369e04f","src/macros/enum_try_as.rs":"8bd311522786f0186290ea31a6f3483aa3fa23ccb3441b7fcf9b3743c9d3670b","src/macros/enum_variant_array.rs":"cb8de5cb5841fa9514c423c195b47a2fc13b9f5fb257093311fbb9c136c737d9","src/macros/enum_variant_names.rs":"ad4d2f87bf26e4d07327896118382a4b83c2cca5de6fdfecca6e7639eb73118b","src/macros/from_repr.rs":"561a6f10372c365674e064b31c6eb4c531ffa0b39f03476452bb0746201d579d","src/macros/mod.rs":"c94b29d4ad0f40a7803605d42647cc9fafec76545c7f3c435fe675a3102eb0ef","src/macros/strings/as_ref_str.rs":"594f29efd111ceef258ab0dd449db4cb52e638e1bd17efe589c5a90ee03686b9","src/macros/strings/display.rs":"30b2c4d4f2f29c98058dd88d004782688241a2608d2976621c6919ea0cf615cb","src/macros/strings/from_string.rs":"53d298f6859df3c13197a8bd303971d2a9ac14cb9860ea1e7c4df1bffdea58bd","src/macros/strings/mod.rs":"b7937f72a46c807fd87c787f3d23029509565a0388a30106e7026363782f2b56","src/macros/strings/to_string.rs":"983e6c1b1dec145acfa0c6a7ef8ff01d30507145753a1d85d0387c5ab61f6fa1"},"package":"c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"}
\ No newline at end of file
diff --git a/crates/strum_macros/Android.bp b/crates/strum_macros/Android.bp
index 344b2b7..fceb38a 100644
--- a/crates/strum_macros/Android.bp
+++ b/crates/strum_macros/Android.bp
@@ -17,7 +17,7 @@
name: "libstrum_macros",
crate_name: "strum_macros",
cargo_env_compat: true,
- cargo_pkg_version: "0.25.3",
+ cargo_pkg_version: "0.26.2",
crate_root: "src/lib.rs",
edition: "2018",
rustlibs: [
@@ -34,7 +34,7 @@
host_cross_supported: false,
crate_name: "strum_macros",
cargo_env_compat: true,
- cargo_pkg_version: "0.25.3",
+ cargo_pkg_version: "0.26.2",
crate_root: "src/lib.rs",
test_suites: ["general-tests"],
auto_gen_config: true,
diff --git a/crates/strum_macros/Cargo.toml b/crates/strum_macros/Cargo.toml
index 45078e7..9748353 100644
--- a/crates/strum_macros/Cargo.toml
+++ b/crates/strum_macros/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "strum_macros"
-version = "0.25.3"
+version = "0.26.2"
authors = ["Peter Glotfelty <peter.glotfelty@microsoft.com>"]
description = "Helpful macros for working with enums and strings"
homepage = "https://github.com/Peternator7/strum"
@@ -55,4 +55,4 @@
]
[dev-dependencies.strum]
-version = "0.25"
+version = "0.26"
diff --git a/crates/strum_macros/METADATA b/crates/strum_macros/METADATA
index 9df3aeb..9e6d739 100644
--- a/crates/strum_macros/METADATA
+++ b/crates/strum_macros/METADATA
@@ -1,17 +1,17 @@
name: "strum_macros"
description: "Helpful macros for working with enums and strings"
third_party {
- version: "0.25.3"
+ version: "0.26.2"
license_type: NOTICE
last_upgrade_date {
year: 2024
- month: 1
- day: 17
+ month: 12
+ day: 20
}
homepage: "https://crates.io/crates/strum_macros"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/strum_macros/strum_macros-0.25.3.crate"
- version: "0.25.3"
+ value: "https://static.crates.io/crates/strum_macros/strum_macros-0.26.2.crate"
+ version: "0.26.2"
}
}
diff --git a/crates/strum_macros/README.md b/crates/strum_macros/README.md
index 491c24f..4099439 100644
--- a/crates/strum_macros/README.md
+++ b/crates/strum_macros/README.md
@@ -22,11 +22,11 @@
```toml
[dependencies]
-strum = "0.25"
-strum_macros = "0.25"
+strum = "0.26"
+strum_macros = "0.26"
# You can also use the "derive" feature, and import the macros directly from "strum"
-# strum = { version = "0.25", features = ["derive"] }
+# strum = { version = "0.26", features = ["derive"] }
```
# Strum Macros
@@ -40,16 +40,21 @@
| [FromRepr] | Convert from an integer to an enum. |
| [AsRefStr] | Implement `AsRef<str>` for `MyEnum` |
| [IntoStaticStr] | Implements `From<MyEnum> for &'static str` on an enum |
-| [EnumVariantNames] | Adds an associated `VARIANTS` constant which is an array of discriminant names |
| [EnumIter] | Creates a new type that iterates of the variants of an enum. |
| [EnumProperty] | Add custom properties to enum variants. |
| [EnumMessage] | Add a verbose message to an enum variant. |
| [EnumDiscriminants] | Generate a new type with only the discriminant names. |
| [EnumCount] | Add a constant `usize` equal to the number of variants. |
+| [VariantArray] | Adds an associated `VARIANTS` constant which is an array of all enum discriminants |
+| [VariantNames] | Adds an associated `VARIANTS` constant which is an array of discriminant names |
+| [EnumTable] | *Experimental*, creates a new type that stores an item of a specified type for each variant of the enum. |
# Contributing
-Thanks for your interest in contributing. The project is divided into 3 parts, the traits are in the
+Thanks for your interest in contributing. Bug fixes are always welcome. If you are interested in implementing or
+adding a macro, please open an issue first to discuss the feature. I have limited bandwidth to review new features.
+
+The project is divided into 3 parts, the traits are in the
`/strum` folder. The procedural macros are in the `/strum_macros` folder, and the integration tests are
in `/strum_tests`. If you are adding additional features to `strum` or `strum_macros`, you should make sure
to run the tests and add new integration tests to make sure the features work as expected.
@@ -67,16 +72,17 @@
Strumming is also a very whimsical motion, much like writing Rust code.
-[Macro-Renames]: https://github.com/Peternator7/strum/wiki/Macro-Renames
-[EnumString]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumString.html
-[Display]: https://docs.rs/strum_macros/0.25/strum_macros/derive.Display.html
-[AsRefStr]: https://docs.rs/strum_macros/0.25/strum_macros/derive.AsRefStr.html
-[IntoStaticStr]: https://docs.rs/strum_macros/0.25/strum_macros/derive.IntoStaticStr.html
-[EnumVariantNames]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumVariantNames.html
-[EnumIter]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumIter.html
-[EnumIs]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumIs.html
-[EnumProperty]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumProperty.html
-[EnumMessage]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumMessage.html
-[EnumDiscriminants]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumDiscriminants.html
-[EnumCount]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumCount.html
-[FromRepr]: https://docs.rs/strum_macros/0.25/strum_macros/derive.FromRepr.html
+[EnumString]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumString.html
+[Display]: https://docs.rs/strum_macros/latest/strum_macros/derive.Display.html
+[AsRefStr]: https://docs.rs/strum_macros/latest/strum_macros/derive.AsRefStr.html
+[IntoStaticStr]: https://docs.rs/strum_macros/latest/strum_macros/derive.IntoStaticStr.html
+[EnumIter]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumIter.html
+[EnumIs]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumIs.html
+[EnumProperty]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumProperty.html
+[EnumMessage]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumMessage.html
+[EnumDiscriminants]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumDiscriminants.html
+[EnumCount]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumCount.html
+[FromRepr]: https://docs.rs/strum_macros/latest/strum_macros/derive.FromRepr.html
+[VariantArray]: https://docs.rs/strum_macros/latest/strum_macros/derive.StaticVariantsArray.html
+[VariantNames]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumVariantNames.html
+[EnumTable]: https://docs.rs/strum_macros/latest/strum_macros/derive.EnumTable.html
diff --git a/crates/strum_macros/src/helpers/case_style.rs b/crates/strum_macros/src/helpers/case_style.rs
index 86a8583..bcea788 100644
--- a/crates/strum_macros/src/helpers/case_style.rs
+++ b/crates/strum_macros/src/helpers/case_style.rs
@@ -1,5 +1,6 @@
use heck::{
- ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, ToUpperCamelCase, ToTrainCase,
+ ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, ToTrainCase,
+ ToUpperCamelCase,
};
use std::str::FromStr;
use syn::{
diff --git a/crates/strum_macros/src/helpers/inner_variant_props.rs b/crates/strum_macros/src/helpers/inner_variant_props.rs
new file mode 100644
index 0000000..bc0fe79
--- /dev/null
+++ b/crates/strum_macros/src/helpers/inner_variant_props.rs
@@ -0,0 +1,33 @@
+use super::metadata::{InnerVariantExt, InnerVariantMeta};
+use super::occurrence_error;
+use syn::{Field, LitStr};
+
+pub trait HasInnerVariantProperties {
+ fn get_variant_inner_properties(&self) -> syn::Result<StrumInnerVariantProperties>;
+}
+
+#[derive(Clone, Eq, PartialEq, Debug, Default)]
+pub struct StrumInnerVariantProperties {
+ pub default_with: Option<LitStr>,
+}
+
+impl HasInnerVariantProperties for Field {
+ fn get_variant_inner_properties(&self) -> syn::Result<StrumInnerVariantProperties> {
+ let mut output = StrumInnerVariantProperties { default_with: None };
+
+ let mut default_with_kw = None;
+ for meta in self.get_named_metadata()? {
+ match meta {
+ InnerVariantMeta::DefaultWith { kw, value } => {
+ if let Some(fst_kw) = default_with_kw {
+ return Err(occurrence_error(fst_kw, kw, "default_with"));
+ }
+ default_with_kw = Some(kw);
+ output.default_with = Some(value);
+ }
+ }
+ }
+
+ Ok(output)
+ }
+}
diff --git a/crates/strum_macros/src/helpers/metadata.rs b/crates/strum_macros/src/helpers/metadata.rs
index d638ae3..94100a7 100644
--- a/crates/strum_macros/src/helpers/metadata.rs
+++ b/crates/strum_macros/src/helpers/metadata.rs
@@ -4,8 +4,8 @@
parse::{Parse, ParseStream},
parse2, parse_str,
punctuated::Punctuated,
- Attribute, DeriveInput, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue, Path,
- Token, Variant, Visibility,
+ Attribute, DeriveInput, Expr, ExprLit, Field, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue,
+ Path, Token, Variant, Visibility,
};
use super::case_style::CaseStyle;
@@ -17,6 +17,7 @@
// enum metadata
custom_keyword!(serialize_all);
custom_keyword!(use_phf);
+ custom_keyword!(prefix);
// enum discriminant metadata
custom_keyword!(derive);
@@ -30,6 +31,7 @@
custom_keyword!(to_string);
custom_keyword!(disabled);
custom_keyword!(default);
+ custom_keyword!(default_with);
custom_keyword!(props);
custom_keyword!(ascii_case_insensitive);
}
@@ -45,6 +47,10 @@
crate_module_path: Path,
},
UsePhf(kw::use_phf),
+ Prefix {
+ kw: kw::prefix,
+ prefix: LitStr,
+ },
}
impl Parse for EnumMeta {
@@ -69,6 +75,11 @@
Ok(EnumMeta::AsciiCaseInsensitive(input.parse()?))
} else if lookahead.peek(kw::use_phf) {
Ok(EnumMeta::UsePhf(input.parse()?))
+ } else if lookahead.peek(kw::prefix) {
+ let kw = input.parse::<kw::prefix>()?;
+ input.parse::<Token![=]>()?;
+ let prefix = input.parse()?;
+ Ok(EnumMeta::Prefix { kw, prefix })
} else {
Err(lookahead.error())
}
@@ -155,6 +166,10 @@
},
Disabled(kw::disabled),
Default(kw::default),
+ DefaultWith {
+ kw: kw::default_with,
+ value: LitStr,
+ },
AsciiCaseInsensitive {
kw: kw::ascii_case_insensitive,
value: bool,
@@ -192,6 +207,11 @@
Ok(VariantMeta::Disabled(input.parse()?))
} else if lookahead.peek(kw::default) {
Ok(VariantMeta::Default(input.parse()?))
+ } else if lookahead.peek(kw::default_with) {
+ let kw = input.parse()?;
+ let _: Token![=] = input.parse()?;
+ let value = input.parse()?;
+ Ok(VariantMeta::DefaultWith { kw, value })
} else if lookahead.peek(kw::ascii_case_insensitive) {
let kw = input.parse()?;
let value = if input.peek(Token![=]) {
@@ -243,7 +263,7 @@
let result = get_metadata_inner("strum", &self.attrs)?;
self.attrs
.iter()
- .filter(|attr| attr.path().is_ident("doc"))
+ .filter(|attr| attr.meta.path().is_ident("doc"))
.try_fold(result, |mut vec, attr| {
if let Meta::NameValue(MetaNameValue {
value:
@@ -274,3 +294,37 @@
Ok(vec)
})
}
+
+#[derive(Debug)]
+pub enum InnerVariantMeta {
+ DefaultWith { kw: kw::default_with, value: LitStr },
+}
+
+impl Parse for InnerVariantMeta {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(kw::default_with) {
+ let kw = input.parse()?;
+ let _: Token![=] = input.parse()?;
+ let value = input.parse()?;
+ Ok(InnerVariantMeta::DefaultWith { kw, value })
+ } else {
+ Err(lookahead.error())
+ }
+ }
+}
+
+pub trait InnerVariantExt {
+ /// Get all the metadata associated with an enum variant inner.
+ fn get_named_metadata(&self) -> syn::Result<Vec<InnerVariantMeta>>;
+}
+
+impl InnerVariantExt for Field {
+ fn get_named_metadata(&self) -> syn::Result<Vec<InnerVariantMeta>> {
+ let result = get_metadata_inner("strum", &self.attrs)?;
+ self.attrs
+ .iter()
+ .filter(|attr| attr.meta.path().is_ident("default_with"))
+ .try_fold(result, |vec, _attr| Ok(vec))
+ }
+}
diff --git a/crates/strum_macros/src/helpers/mod.rs b/crates/strum_macros/src/helpers/mod.rs
index 142ea0b..23d60b5 100644
--- a/crates/strum_macros/src/helpers/mod.rs
+++ b/crates/strum_macros/src/helpers/mod.rs
@@ -1,8 +1,10 @@
-pub use self::case_style::{CaseStyleHelpers, snakify};
+pub use self::case_style::snakify;
+pub use self::inner_variant_props::HasInnerVariantProperties;
pub use self::type_props::HasTypeProperties;
pub use self::variant_props::HasStrumVariantProperties;
pub mod case_style;
+pub mod inner_variant_props;
mod metadata;
pub mod type_props;
pub mod variant_props;
@@ -15,6 +17,14 @@
syn::Error::new(Span::call_site(), "This macro only supports enums.")
}
+pub fn non_unit_variant_error() -> syn::Error {
+ syn::Error::new(
+ Span::call_site(),
+ "This macro only supports enums of strictly unit variants. Consider \
+ using it in conjunction with [`EnumDiscriminants`]",
+ )
+}
+
pub fn strum_discriminants_passthrough_error(span: &impl Spanned) -> syn::Error {
syn::Error::new(
span.span(),
diff --git a/crates/strum_macros/src/helpers/type_props.rs b/crates/strum_macros/src/helpers/type_props.rs
index 0d49e04..7302853 100644
--- a/crates/strum_macros/src/helpers/type_props.rs
+++ b/crates/strum_macros/src/helpers/type_props.rs
@@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use quote::quote;
use std::default::Default;
-use syn::{parse_quote, DeriveInput, Ident, Path, Visibility};
+use syn::{parse_quote, DeriveInput, Ident, LitStr, Path, Visibility};
use super::case_style::CaseStyle;
use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta};
@@ -21,6 +21,8 @@
pub discriminant_others: Vec<TokenStream>,
pub discriminant_vis: Option<Visibility>,
pub use_phf: bool,
+ pub prefix: Option<LitStr>,
+ pub enum_repr: Option<TokenStream>,
}
impl HasTypeProperties for DeriveInput {
@@ -34,6 +36,7 @@
let mut ascii_case_insensitive_kw = None;
let mut use_phf_kw = None;
let mut crate_module_path_kw = None;
+ let mut prefix_kw = None;
for meta in strum_meta {
match meta {
EnumMeta::SerializeAll { case_style, kw } => {
@@ -71,6 +74,14 @@
crate_module_path_kw = Some(kw);
output.crate_module_path = Some(crate_module_path);
}
+ EnumMeta::Prefix { prefix, kw } => {
+ if let Some(fst_kw) = prefix_kw {
+ return Err(occurrence_error(fst_kw, kw, "prefix"));
+ }
+
+ prefix_kw = Some(kw);
+ output.prefix = Some(prefix);
+ }
}
}
@@ -103,6 +114,17 @@
}
}
+ let attrs = &self.attrs;
+ for attr in attrs {
+ if let Ok(list) = attr.meta.require_list() {
+ if let Some(ident) = list.path.get_ident() {
+ if ident == "repr" {
+ output.enum_repr = Some(list.tokens.clone())
+ }
+ }
+ }
+ }
+
Ok(output)
}
}
diff --git a/crates/strum_macros/src/helpers/variant_props.rs b/crates/strum_macros/src/helpers/variant_props.rs
index f637253..681c7c0 100644
--- a/crates/strum_macros/src/helpers/variant_props.rs
+++ b/crates/strum_macros/src/helpers/variant_props.rs
@@ -13,6 +13,7 @@
pub struct StrumVariantProperties {
pub disabled: Option<kw::disabled>,
pub default: Option<kw::default>,
+ pub default_with: Option<LitStr>,
pub ascii_case_insensitive: Option<bool>,
pub message: Option<LitStr>,
pub detailed_message: Option<LitStr>,
@@ -29,14 +30,24 @@
LitStr::new(&ident.convert_case(case_style), ident.span())
}
- pub fn get_preferred_name(&self, case_style: Option<CaseStyle>) -> LitStr {
- self.to_string.as_ref().cloned().unwrap_or_else(|| {
+ pub fn get_preferred_name(
+ &self,
+ case_style: Option<CaseStyle>,
+ prefix: Option<&LitStr>,
+ ) -> LitStr {
+ let mut output = self.to_string.as_ref().cloned().unwrap_or_else(|| {
self.serialize
.iter()
.max_by_key(|s| s.value().len())
.cloned()
.unwrap_or_else(|| self.ident_as_str(case_style))
- })
+ });
+
+ if let Some(prefix) = prefix {
+ output = LitStr::new(&(prefix.value() + &output.value()), output.span());
+ }
+
+ output
}
pub fn get_serializations(&self, case_style: Option<CaseStyle>) -> Vec<LitStr> {
@@ -62,9 +73,10 @@
let mut message_kw = None;
let mut detailed_message_kw = None;
- let mut to_string_kw = None;
let mut disabled_kw = None;
let mut default_kw = None;
+ let mut default_with_kw = None;
+ let mut to_string_kw = None;
let mut ascii_case_insensitive_kw = None;
for meta in self.get_metadata()? {
match meta {
@@ -114,6 +126,14 @@
default_kw = Some(kw);
output.default = Some(kw);
}
+ VariantMeta::DefaultWith { kw, value } => {
+ if let Some(fst_kw) = default_with_kw {
+ return Err(occurrence_error(fst_kw, kw, "default_with"));
+ }
+
+ default_with_kw = Some(kw);
+ output.default_with = Some(value);
+ }
VariantMeta::AsciiCaseInsensitive { kw, value } => {
if let Some(fst_kw) = ascii_case_insensitive_kw {
return Err(occurrence_error(fst_kw, kw, "ascii_case_insensitive"));
diff --git a/crates/strum_macros/src/lib.rs b/crates/strum_macros/src/lib.rs
index 82db12a..b337fb3 100644
--- a/crates/strum_macros/src/lib.rs
+++ b/crates/strum_macros/src/lib.rs
@@ -44,7 +44,7 @@
/// variant. There is an option to match on different case conversions through the
/// `#[strum(serialize_all = "snake_case")]` type attribute.
///
-/// See the [Additional Attributes](https://docs.rs/strum/0.22/strum/additional_attributes/index.html)
+/// See the [Additional Attributes](https://docs.rs/strum/latest/strum/additional_attributes/index.html)
/// Section for more information on using this feature.
///
/// If you have a large enum, you may want to consider using the `use_phf` attribute here. It leverages
@@ -118,12 +118,15 @@
toks.into()
}
-/// Converts enum variants to `&'static str`.
+/// Converts enum variants to `&'a str`, where `'a` is the lifetime of the input enum reference.
///
/// Implements `AsRef<str>` on your enum using the same rules as
/// `Display` for determining what string is returned. The difference is that `as_ref()` returns
/// a `&str` instead of a `String` so you don't allocate any additional memory with each call.
///
+/// If you require a `&'static str`, you can use
+/// [`strum::IntoStaticStr`](crate::IntoStaticStr) instead.
+///
/// ```
/// // You need to bring the AsRef trait into scope to use it
/// use std::convert::AsRef;
@@ -152,6 +155,18 @@
/// Color::Blue(10).as_ref(),
/// Color::Green { range: 42 }.as_ref()
/// );
+///
+/// // With prefix on all variants
+/// #[derive(AsRefStr, Debug)]
+/// #[strum(prefix = "/")]
+/// enum ColorWithPrefix {
+/// #[strum(serialize = "redred")]
+/// Red,
+/// Green,
+/// }
+///
+/// assert_eq!("/redred", ColorWithPrefix::Red.as_ref());
+/// assert_eq!("/Green", ColorWithPrefix::Green.as_ref());
/// ```
#[proc_macro_derive(AsRefStr, attributes(strum))]
pub fn as_ref_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -163,18 +178,18 @@
toks.into()
}
-/// Implements `Strum::VariantNames` which adds an associated constant `VARIANTS` which is an array of discriminant names.
+/// Implements `Strum::VariantNames` which adds an associated constant `VARIANTS` which is a `'static` slice of discriminant names.
///
/// Adds an `impl` block for the `enum` that adds a static `VARIANTS` array of `&'static str` that are the discriminant names.
/// This will respect the `serialize_all` attribute on the `enum` (like `#[strum(serialize_all = "snake_case")]`.
///
/// ```
/// // import the macros needed
-/// use strum_macros::{EnumString, EnumVariantNames};
+/// use strum_macros::{EnumString};
/// // You need to import the trait, to have access to VARIANTS
/// use strum::VariantNames;
///
-/// #[derive(Debug, EnumString, EnumVariantNames)]
+/// #[derive(Debug, EnumString, strum_macros::VariantNames)]
/// #[strum(serialize_all = "kebab-case")]
/// enum Color {
/// Red,
@@ -184,7 +199,7 @@
/// }
/// assert_eq!(["red", "blue", "yellow", "rebecca-purple"], Color::VARIANTS);
/// ```
-#[proc_macro_derive(EnumVariantNames, attributes(strum))]
+#[proc_macro_derive(VariantNames, attributes(strum))]
pub fn variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(input as DeriveInput);
@@ -194,7 +209,54 @@
toks.into()
}
+#[doc(hidden)]
+#[proc_macro_derive(EnumVariantNames, attributes(strum))]
+#[deprecated(
+ since = "0.26.0",
+ note = "please use `#[derive(VariantNames)]` instead"
+)]
+pub fn variant_names_deprecated(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let ast = syn::parse_macro_input!(input as DeriveInput);
+
+ let toks = macros::enum_variant_names::enum_variant_names_inner(&ast)
+ .unwrap_or_else(|err| err.to_compile_error());
+ debug_print_generated(&ast, &toks);
+ toks.into()
+}
+
+/// Adds a `'static` slice with all of the Enum's variants.
+///
+/// Implements `strum::VariantArray` which adds an associated constant `VARIANTS`.
+/// This constant contains an array with all the variants of the enumerator.
+///
+/// This trait can only be autoderived if the enumerator is composed only of unit-type variants,
+/// meaning that the variants must not have any data.
+///
+/// ```
+/// use strum::VariantArray;
+///
+/// #[derive(VariantArray, Debug, PartialEq, Eq)]
+/// enum Op {
+/// Add,
+/// Sub,
+/// Mul,
+/// Div,
+/// }
+///
+/// assert_eq!(Op::VARIANTS, &[Op::Add, Op::Sub, Op::Mul, Op::Div]);
+/// ```
+#[proc_macro_derive(VariantArray, attributes(strum))]
+pub fn static_variants_array(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let ast = syn::parse_macro_input!(input as DeriveInput);
+
+ let toks = macros::enum_variant_array::static_variants_array_inner(&ast)
+ .unwrap_or_else(|err| err.to_compile_error());
+ debug_print_generated(&ast, &toks);
+ toks.into()
+}
+
#[proc_macro_derive(AsStaticStr, attributes(strum))]
+#[doc(hidden)]
#[deprecated(
since = "0.22.0",
note = "please use `#[derive(IntoStaticStr)]` instead"
@@ -282,6 +344,7 @@
since = "0.22.0",
note = "please use `#[derive(Display)]` instead. See issue https://github.com/Peternator7/strum/issues/132"
)]
+#[doc(hidden)]
#[proc_macro_derive(ToString, attributes(strum))]
pub fn to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(input as DeriveInput);
@@ -299,9 +362,21 @@
/// choose which serialization to used based on the following criteria:
///
/// 1. If there is a `to_string` property, this value will be used. There can only be one per variant.
-/// 1. Of the various `serialize` properties, the value with the longest length is chosen. If that
+/// 2. Of the various `serialize` properties, the value with the longest length is chosen. If that
/// behavior isn't desired, you should use `to_string`.
-/// 1. The name of the variant will be used if there are no `serialize` or `to_string` attributes.
+/// 3. The name of the variant will be used if there are no `serialize` or `to_string` attributes.
+/// 4. If the enum has a `strum(prefix = "some_value_")`, every variant will have that prefix prepended
+/// to the serialization.
+/// 5. Enums with named fields support named field interpolation. The value will be interpolated into the output string.
+/// Note this means the variant will not "round trip" if you then deserialize the string.
+///
+/// ```rust
+/// #[derive(strum_macros::Display)]
+/// pub enum Color {
+/// #[strum(to_string = "saturation is {sat}")]
+/// Red { sat: usize },
+/// }
+/// ```
///
/// ```
/// // You need to bring the ToString trait into scope to use it
@@ -317,6 +392,10 @@
/// },
/// Blue(usize),
/// Yellow,
+/// #[strum(to_string = "purple with {sat} saturation")]
+/// Purple {
+/// sat: usize,
+/// },
/// }
///
/// // uses the serialize string for Display
@@ -331,6 +410,9 @@
/// Color::Blue(10),
/// Color::Green { range: 42 }
/// );
+/// // you can also use named fields in message
+/// let purple = Color::Purple { sat: 10 };
+/// assert_eq!(String::from("purple with 10 saturation"), purple.to_string());
/// ```
#[proc_macro_derive(Display, attributes(strum))]
pub fn display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -444,6 +526,60 @@
toks.into()
}
+/// Creates a new type that maps all the variants of an enum to another generic value.
+///
+/// This macro only supports enums with unit type variants.A new type called `YourEnumTable<T>`. Essentially, it's a wrapper
+/// `[T; YourEnum::Count]` where gets/sets are infallible. Some important caveats to note:
+///
+/// * The size of `YourEnumTable<T>` increases with the number of variants, not the number of values because it's always
+/// fully populated. This means it may not be a good choice for sparsely populated maps.
+///
+/// * Lookups are basically constant time since it's functionally an array index.
+///
+/// * Your variants cannot have associated data. You can use `EnumDiscriminants` to generate an Enum with the same
+/// names to work around this.
+///
+/// # Stability
+///
+/// Several people expressed interest in a data structure like this and pushed the PR through to completion, but the api
+/// seems incomplete, and I reserve the right to deprecate it in the future if it becomes clear the design is flawed.
+///
+/// # Example
+/// ```rust
+/// use strum_macros::EnumTable;
+///
+/// #[derive(EnumTable)]
+/// enum Color {
+/// Red,
+/// Yellow,
+/// Green,
+/// Blue,
+/// }
+///
+/// assert_eq!(ColorTable::default(), ColorTable::new(0, 0, 0, 0));
+/// assert_eq!(ColorTable::filled(2), ColorTable::new(2, 2, 2, 2));
+/// assert_eq!(ColorTable::from_closure(|_| 3), ColorTable::new(3, 3, 3, 3));
+/// assert_eq!(ColorTable::default().transform(|_, val| val + 2), ColorTable::new(2, 2, 2, 2));
+///
+/// let mut complex_map = ColorTable::from_closure(|color| match color {
+/// Color::Red => 0,
+/// _ => 3
+/// });
+///
+/// complex_map[Color::Green] = complex_map[Color::Red];
+/// assert_eq!(complex_map, ColorTable::new(0, 3, 0, 3));
+/// ```
+#[doc(hidden)]
+#[proc_macro_derive(EnumTable, attributes(strum))]
+pub fn enum_table(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let ast = syn::parse_macro_input!(input as DeriveInput);
+
+ let toks =
+ macros::enum_table::enum_table_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
+ debug_print_generated(&ast, &toks);
+ toks.into()
+}
+
/// Add a function to enum that allows accessing variants by its discriminant
///
/// This macro adds a standalone function to obtain an enum variant by its discriminant. The macro adds
@@ -518,7 +654,6 @@
/// assert_eq!(Some(Number::Three), number_from_repr(3));
/// assert_eq!(None, number_from_repr(4));
/// ```
-
#[proc_macro_derive(FromRepr, attributes(strum))]
pub fn from_repr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(input as DeriveInput);
@@ -690,7 +825,7 @@
/// // Bring trait into scope
/// use std::str::FromStr;
/// use strum::{IntoEnumIterator, EnumMessage};
-/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString, EnumMessage};
+/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString};
///
/// #[derive(Debug)]
/// struct NonDefault;
diff --git a/crates/strum_macros/src/macros/enum_discriminants.rs b/crates/strum_macros/src/macros/enum_discriminants.rs
index 4e54a30..f7d6372 100644
--- a/crates/strum_macros/src/macros/enum_discriminants.rs
+++ b/crates/strum_macros/src/macros/enum_discriminants.rs
@@ -40,10 +40,16 @@
// Pass through all other attributes
let pass_though_attributes = type_properties.discriminant_others;
+ let repr = type_properties.enum_repr.map(|repr| quote!(#[repr(#repr)]));
+
// Add the variants without fields, but exclude the `strum` meta item
let mut discriminants = Vec::new();
for variant in variants {
let ident = &variant.ident;
+ let discriminant = variant
+ .discriminant
+ .as_ref()
+ .map(|(_, expr)| quote!( = #expr));
// Don't copy across the "strum" meta attribute. Only passthrough the whitelisted
// attributes and proxy `#[strum_discriminants(...)]` attributes
@@ -81,7 +87,7 @@
})
.collect::<Result<Vec<_>, _>>()?;
- discriminants.push(quote! { #(#attrs)* #ident });
+ discriminants.push(quote! { #(#attrs)* #ident #discriminant});
}
// Ideally:
@@ -153,6 +159,7 @@
Ok(quote! {
/// Auto-generated discriminant enum variants
#derives
+ #repr
#(#[ #pass_though_attributes ])*
#discriminants_vis enum #discriminants_name {
#(#discriminants),*
diff --git a/crates/strum_macros/src/macros/enum_is.rs b/crates/strum_macros/src/macros/enum_is.rs
index ecada45..c239628 100644
--- a/crates/strum_macros/src/macros/enum_is.rs
+++ b/crates/strum_macros/src/macros/enum_is.rs
@@ -1,4 +1,4 @@
-use crate::helpers::{non_enum_error, snakify, HasStrumVariantProperties};
+use crate::helpers::{case_style::snakify, non_enum_error, HasStrumVariantProperties};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{Data, DeriveInput};
@@ -20,7 +20,10 @@
let variant_name = &variant.ident;
let fn_name = format_ident!("is_{}", snakify(&variant_name.to_string()));
- let doc_comment = format!("Returns [true] if the enum is [{}::{}] otherwise [false]", enum_name, variant_name);
+ let doc_comment = format!(
+ "Returns [true] if the enum is [{}::{}] otherwise [false]",
+ enum_name, variant_name
+ );
Some(quote! {
#[must_use]
#[inline]
diff --git a/crates/strum_macros/src/macros/enum_iter.rs b/crates/strum_macros/src/macros/enum_iter.rs
index 0e700aa..5e81001 100644
--- a/crates/strum_macros/src/macros/enum_iter.rs
+++ b/crates/strum_macros/src/macros/enum_iter.rs
@@ -74,7 +74,7 @@
#[allow(
missing_copy_implementations,
)]
- #vis struct #iter_name #ty_generics {
+ #vis struct #iter_name #impl_generics {
idx: usize,
back_idx: usize,
marker: ::core::marker::PhantomData #phantom_data,
@@ -159,6 +159,8 @@
}
}
+ impl #impl_generics ::core::iter::FusedIterator for #iter_name #ty_generics #where_clause { }
+
impl #impl_generics Clone for #iter_name #ty_generics #where_clause {
fn clone(&self) -> #iter_name #ty_generics {
#iter_name {
diff --git a/crates/strum_macros/src/macros/enum_messages.rs b/crates/strum_macros/src/macros/enum_messages.rs
index c056108..2dad411 100644
--- a/crates/strum_macros/src/macros/enum_messages.rs
+++ b/crates/strum_macros/src/macros/enum_messages.rs
@@ -74,14 +74,17 @@
if !documentation.is_empty() {
let params = params.clone();
// Strip a single leading space from each documentation line.
- let documentation: Vec<LitStr> = documentation.iter().map(|lit_str| {
- let line = lit_str.value();
- if line.starts_with(' ') {
- LitStr::new(&line.as_str()[1..], lit_str.span())
- } else {
- lit_str.clone()
- }
- }).collect();
+ let documentation: Vec<LitStr> = documentation
+ .iter()
+ .map(|lit_str| {
+ let line = lit_str.value();
+ if line.starts_with(' ') {
+ LitStr::new(&line.as_str()[1..], lit_str.span())
+ } else {
+ lit_str.clone()
+ }
+ })
+ .collect();
if documentation.len() == 1 {
let text = &documentation[0];
documentation_arms
diff --git a/crates/strum_macros/src/macros/enum_table.rs b/crates/strum_macros/src/macros/enum_table.rs
new file mode 100644
index 0000000..f9d4e81
--- /dev/null
+++ b/crates/strum_macros/src/macros/enum_table.rs
@@ -0,0 +1,204 @@
+use proc_macro2::{Span, TokenStream};
+use quote::{format_ident, quote};
+use syn::{spanned::Spanned, Data, DeriveInput, Fields};
+
+use crate::helpers::{non_enum_error, snakify, HasStrumVariantProperties};
+
+pub fn enum_table_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
+ let name = &ast.ident;
+ let gen = &ast.generics;
+ let vis = &ast.vis;
+ let mut doc_comment = format!("A map over the variants of `{}`", name);
+
+ if gen.lifetimes().count() > 0 {
+ return Err(syn::Error::new(
+ Span::call_site(),
+ "`EnumTable` doesn't support enums with lifetimes.",
+ ));
+ }
+
+ let variants = match &ast.data {
+ Data::Enum(v) => &v.variants,
+ _ => return Err(non_enum_error()),
+ };
+
+ let table_name = format_ident!("{}Table", name);
+
+ // the identifiers of each variant, in PascalCase
+ let mut pascal_idents = Vec::new();
+ // the identifiers of each struct field, in snake_case
+ let mut snake_idents = Vec::new();
+ // match arms in the form `MyEnumTable::Variant => &self.variant,`
+ let mut get_matches = Vec::new();
+ // match arms in the form `MyEnumTable::Variant => &mut self.variant,`
+ let mut get_matches_mut = Vec::new();
+ // match arms in the form `MyEnumTable::Variant => self.variant = new_value`
+ let mut set_matches = Vec::new();
+ // struct fields of the form `variant: func(MyEnum::Variant),*
+ let mut closure_fields = Vec::new();
+ // struct fields of the form `variant: func(MyEnum::Variant, self.variant),`
+ let mut transform_fields = Vec::new();
+
+ // identifiers for disabled variants
+ let mut disabled_variants = Vec::new();
+ // match arms for disabled variants
+ let mut disabled_matches = Vec::new();
+
+ for variant in variants {
+ // skip disabled variants
+ if variant.get_variant_properties()?.disabled.is_some() {
+ let disabled_ident = &variant.ident;
+ let panic_message = format!(
+ "Can't use `{}` with `{}` - variant is disabled for Strum features",
+ disabled_ident, table_name
+ );
+ disabled_variants.push(disabled_ident);
+ disabled_matches.push(quote!(#name::#disabled_ident => panic!(#panic_message),));
+ continue;
+ }
+
+ // Error on variants with data
+ if variant.fields != Fields::Unit {
+ return Err(syn::Error::new(
+ variant.fields.span(),
+ "`EnumTable` doesn't support enums with non-unit variants",
+ ));
+ };
+
+ let pascal_case = &variant.ident;
+ let snake_case = format_ident!("_{}", snakify(&pascal_case.to_string()));
+
+ get_matches.push(quote! {#name::#pascal_case => &self.#snake_case,});
+ get_matches_mut.push(quote! {#name::#pascal_case => &mut self.#snake_case,});
+ set_matches.push(quote! {#name::#pascal_case => self.#snake_case = new_value,});
+ closure_fields.push(quote! {#snake_case: func(#name::#pascal_case),});
+ transform_fields.push(quote! {#snake_case: func(#name::#pascal_case, &self.#snake_case),});
+ pascal_idents.push(pascal_case);
+ snake_idents.push(snake_case);
+ }
+
+ // Error on empty enums
+ if pascal_idents.is_empty() {
+ return Err(syn::Error::new(
+ variants.span(),
+ "`EnumTable` requires at least one non-disabled variant",
+ ));
+ }
+
+ // if the index operation can panic, add that to the documentation
+ if !disabled_variants.is_empty() {
+ doc_comment.push_str(&format!(
+ "\n# Panics\nIndexing `{}` with any of the following variants will cause a panic:",
+ table_name
+ ));
+ for variant in disabled_variants {
+ doc_comment.push_str(&format!("\n\n- `{}::{}`", name, variant));
+ }
+ }
+
+ let doc_new = format!(
+ "Create a new {} with a value for each variant of {}",
+ table_name, name
+ );
+ let doc_closure = format!(
+ "Create a new {} by running a function on each variant of `{}`",
+ table_name, name
+ );
+ let doc_transform = format!("Create a new `{}` by running a function on each variant of `{}` and the corresponding value in the current `{0}`", table_name, name);
+ let doc_filled = format!(
+ "Create a new `{}` with the same value in each field.",
+ table_name
+ );
+ let doc_option_all = format!("Converts `{}<Option<T>>` into `Option<{0}<T>>`. Returns `Some` if all fields are `Some`, otherwise returns `None`.", table_name);
+ let doc_result_all_ok = format!("Converts `{}<Result<T, E>>` into `Result<{0}<T>, E>`. Returns `Ok` if all fields are `Ok`, otherwise returns `Err`.", table_name);
+
+ Ok(quote! {
+ #[doc = #doc_comment]
+ #[allow(
+ missing_copy_implementations,
+ )]
+ #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
+ #vis struct #table_name<T> {
+ #(#snake_idents: T,)*
+ }
+
+ impl<T: Clone> #table_name<T> {
+ #[doc = #doc_filled]
+ #vis fn filled(value: T) -> #table_name<T> {
+ #table_name {
+ #(#snake_idents: value.clone(),)*
+ }
+ }
+ }
+
+ impl<T> #table_name<T> {
+ #[doc = #doc_new]
+ #vis fn new(
+ #(#snake_idents: T,)*
+ ) -> #table_name<T> {
+ #table_name {
+ #(#snake_idents,)*
+ }
+ }
+
+ #[doc = #doc_closure]
+ #vis fn from_closure<F: Fn(#name)->T>(func: F) -> #table_name<T> {
+ #table_name {
+ #(#closure_fields)*
+ }
+ }
+
+ #[doc = #doc_transform]
+ #vis fn transform<U, F: Fn(#name, &T)->U>(&self, func: F) -> #table_name<U> {
+ #table_name {
+ #(#transform_fields)*
+ }
+ }
+
+ }
+
+ impl<T> ::core::ops::Index<#name> for #table_name<T> {
+ type Output = T;
+
+ fn index(&self, idx: #name) -> &T {
+ match idx {
+ #(#get_matches)*
+ #(#disabled_matches)*
+ }
+ }
+ }
+
+ impl<T> ::core::ops::IndexMut<#name> for #table_name<T> {
+ fn index_mut(&mut self, idx: #name) -> &mut T {
+ match idx {
+ #(#get_matches_mut)*
+ #(#disabled_matches)*
+ }
+ }
+ }
+
+ impl<T> #table_name<::core::option::Option<T>> {
+ #[doc = #doc_option_all]
+ #vis fn all(self) -> ::core::option::Option<#table_name<T>> {
+ if let #table_name {
+ #(#snake_idents: ::core::option::Option::Some(#snake_idents),)*
+ } = self {
+ ::core::option::Option::Some(#table_name {
+ #(#snake_idents,)*
+ })
+ } else {
+ ::core::option::Option::None
+ }
+ }
+ }
+
+ impl<T, E> #table_name<::core::result::Result<T, E>> {
+ #[doc = #doc_result_all_ok]
+ #vis fn all_ok(self) -> ::core::result::Result<#table_name<T>, E> {
+ ::core::result::Result::Ok(#table_name {
+ #(#snake_idents: self.#snake_idents?,)*
+ })
+ }
+ }
+ })
+}
diff --git a/crates/strum_macros/src/macros/enum_try_as.rs b/crates/strum_macros/src/macros/enum_try_as.rs
index 088a1f1..c6d0127 100644
--- a/crates/strum_macros/src/macros/enum_try_as.rs
+++ b/crates/strum_macros/src/macros/enum_try_as.rs
@@ -1,4 +1,4 @@
-use crate::helpers::{non_enum_error, snakify, HasStrumVariantProperties};
+use crate::helpers::{case_style::snakify, non_enum_error, HasStrumVariantProperties};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{Data, DeriveInput};
@@ -10,6 +10,7 @@
};
let enum_name = &ast.ident;
+ let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let variants: Vec<_> = variants
.iter()
@@ -72,9 +73,8 @@
.collect();
Ok(quote! {
- impl #enum_name {
+ impl #impl_generics #enum_name #ty_generics #where_clause {
#(#variants)*
}
- }
- .into())
+ })
}
diff --git a/crates/strum_macros/src/macros/enum_variant_array.rs b/crates/strum_macros/src/macros/enum_variant_array.rs
new file mode 100644
index 0000000..a6974d6
--- /dev/null
+++ b/crates/strum_macros/src/macros/enum_variant_array.rs
@@ -0,0 +1,34 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{Data, DeriveInput, Fields};
+
+use crate::helpers::{non_enum_error, non_unit_variant_error, HasTypeProperties};
+
+pub fn static_variants_array_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
+ let name = &ast.ident;
+ let gen = &ast.generics;
+ let (impl_generics, ty_generics, where_clause) = gen.split_for_impl();
+
+ let variants = match &ast.data {
+ Data::Enum(v) => &v.variants,
+ _ => return Err(non_enum_error()),
+ };
+
+ let type_properties = ast.get_type_properties()?;
+ let strum_module_path = type_properties.crate_module_path();
+
+ let idents = variants
+ .iter()
+ .cloned()
+ .map(|v| match v.fields {
+ Fields::Unit => Ok(v.ident),
+ _ => Err(non_unit_variant_error()),
+ })
+ .collect::<syn::Result<Vec<_>>>()?;
+
+ Ok(quote! {
+ impl #impl_generics #strum_module_path::VariantArray for #name #ty_generics #where_clause {
+ const VARIANTS: &'static [Self] = &[ #(#name::#idents),* ];
+ }
+ })
+}
diff --git a/crates/strum_macros/src/macros/enum_variant_names.rs b/crates/strum_macros/src/macros/enum_variant_names.rs
index c54d45d..8c41cc4 100644
--- a/crates/strum_macros/src/macros/enum_variant_names.rs
+++ b/crates/strum_macros/src/macros/enum_variant_names.rs
@@ -1,6 +1,6 @@
use proc_macro2::TokenStream;
use quote::quote;
-use syn::{Data, DeriveInput};
+use syn::{Data, DeriveInput, LitStr};
use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
@@ -22,9 +22,10 @@
.iter()
.map(|v| {
let props = v.get_variant_properties()?;
- Ok(props.get_preferred_name(type_properties.case_style))
+ Ok(props
+ .get_preferred_name(type_properties.case_style, type_properties.prefix.as_ref()))
})
- .collect::<syn::Result<Vec<_>>>()?;
+ .collect::<syn::Result<Vec<LitStr>>>()?;
Ok(quote! {
impl #impl_generics #strum_module_path::VariantNames for #name #ty_generics #where_clause {
diff --git a/crates/strum_macros/src/macros/from_repr.rs b/crates/strum_macros/src/macros/from_repr.rs
index 92fd7ad..78bbb28 100644
--- a/crates/strum_macros/src/macros/from_repr.rs
+++ b/crates/strum_macros/src/macros/from_repr.rs
@@ -1,62 +1,31 @@
-use heck::ToShoutySnakeCase;
use proc_macro2::{Span, TokenStream};
-use quote::{format_ident, quote, ToTokens};
-use syn::{Data, DeriveInput, Fields, PathArguments, Type, TypeParen};
+use quote::{format_ident, quote};
+use syn::{Data, DeriveInput, Fields, Type};
-use crate::helpers::{non_enum_error, HasStrumVariantProperties};
+use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
pub fn from_repr_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
let name = &ast.ident;
let gen = &ast.generics;
let (impl_generics, ty_generics, where_clause) = gen.split_for_impl();
let vis = &ast.vis;
- let attrs = &ast.attrs;
let mut discriminant_type: Type = syn::parse("usize".parse().unwrap()).unwrap();
- for attr in attrs {
- let path = attr.path();
-
- let mut ts = if let Ok(ts) = attr
- .meta
- .require_list()
- .map(|metas| metas.to_token_stream().into_iter())
- {
- ts
- } else {
- continue;
- };
- // Discard the path
- let _ = ts.next();
- let tokens: TokenStream = ts.collect();
-
- if path.leading_colon.is_some() {
- continue;
- }
- if path.segments.len() != 1 {
- continue;
- }
- let segment = path.segments.first().unwrap();
- if segment.ident != "repr" {
- continue;
- }
- if segment.arguments != PathArguments::None {
- continue;
- }
- let typ_paren = match syn::parse2::<Type>(tokens.clone()) {
- Ok(Type::Paren(TypeParen { elem, .. })) => *elem,
- _ => continue,
- };
- let inner_path = match &typ_paren {
- Type::Path(t) => t,
- _ => continue,
- };
- if let Some(seg) = inner_path.path.segments.last() {
- for t in &[
- "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize",
- ] {
- if seg.ident == t {
- discriminant_type = typ_paren;
- break;
+ if let Some(type_path) = ast
+ .get_type_properties()
+ .ok()
+ .and_then(|tp| tp.enum_repr)
+ .and_then(|repr_ts| syn::parse2::<Type>(repr_ts).ok())
+ {
+ if let Type::Path(path) = type_path.clone() {
+ if let Some(seg) = path.path.segments.last() {
+ for t in &[
+ "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize",
+ ] {
+ if seg.ident == t {
+ discriminant_type = type_path;
+ break;
+ }
}
}
}
@@ -103,7 +72,7 @@
}
};
- let const_var_str = format!("{}_DISCRIMINANT", variant.ident).to_shouty_snake_case();
+ let const_var_str = format!("{}_DISCRIMINANT", variant.ident);
let const_var_ident = format_ident!("{}", const_var_str);
let const_val_expr = match &variant.discriminant {
@@ -114,7 +83,10 @@
},
};
- constant_defs.push(quote! {const #const_var_ident: #discriminant_type = #const_val_expr;});
+ constant_defs.push(quote! {
+ #[allow(non_upper_case_globals)]
+ const #const_var_ident: #discriminant_type = #const_val_expr;
+ });
arms.push(quote! {v if v == #const_var_ident => ::core::option::Option::Some(#name::#ident #params)});
prev_const_var_ident = Some(const_var_ident);
diff --git a/crates/strum_macros/src/macros/mod.rs b/crates/strum_macros/src/macros/mod.rs
index 8df8cd6..fafc048 100644
--- a/crates/strum_macros/src/macros/mod.rs
+++ b/crates/strum_macros/src/macros/mod.rs
@@ -4,7 +4,9 @@
pub mod enum_iter;
pub mod enum_messages;
pub mod enum_properties;
+pub mod enum_table;
pub mod enum_try_as;
+pub mod enum_variant_array;
pub mod enum_variant_names;
pub mod from_repr;
diff --git a/crates/strum_macros/src/macros/strings/as_ref_str.rs b/crates/strum_macros/src/macros/strings/as_ref_str.rs
index 617b887..8d44f81 100644
--- a/crates/strum_macros/src/macros/strings/as_ref_str.rs
+++ b/crates/strum_macros/src/macros/strings/as_ref_str.rs
@@ -25,7 +25,9 @@
// Look at all the serialize attributes.
// Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent
// (i.e. always `enum.as_ref().to_string() == enum.to_string()`).
- let output = variant_properties.get_preferred_name(type_properties.case_style);
+ let output = variant_properties
+ .get_preferred_name(type_properties.case_style, type_properties.prefix.as_ref());
+
let params = match variant.fields {
Fields::Unit => quote! {},
Fields::Unnamed(..) => quote! { (..) },
diff --git a/crates/strum_macros/src/macros/strings/display.rs b/crates/strum_macros/src/macros/strings/display.rs
index fcc5936..a2f6f24 100644
--- a/crates/strum_macros/src/macros/strings/display.rs
+++ b/crates/strum_macros/src/macros/strings/display.rs
@@ -1,6 +1,6 @@
-use proc_macro2::TokenStream;
+use proc_macro2::{Ident, TokenStream};
use quote::quote;
-use syn::{Data, DeriveInput, Fields};
+use syn::{punctuated::Punctuated, Data, DeriveInput, Fields, LitStr, Token};
use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
@@ -24,18 +24,31 @@
}
// Look at all the serialize attributes.
- let output = variant_properties.get_preferred_name(type_properties.case_style);
+ let output = variant_properties
+ .get_preferred_name(type_properties.case_style, type_properties.prefix.as_ref());
let params = match variant.fields {
Fields::Unit => quote! {},
Fields::Unnamed(..) => quote! { (..) },
- Fields::Named(..) => quote! { {..} },
+ Fields::Named(ref field_names) => {
+ // Transform named params '{ name: String, age: u8 }' to '{ ref name, ref age }'
+ let names: Punctuated<TokenStream, Token!(,)> = field_names
+ .named
+ .iter()
+ .map(|field| {
+ let ident = field.ident.as_ref().unwrap();
+ quote! { ref #ident }
+ })
+ .collect();
+
+ quote! { {#names} }
+ }
};
if variant_properties.to_string.is_none() && variant_properties.default.is_some() {
match &variant.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
- arms.push(quote! { #name::#ident(ref s) => f.pad(s) });
+ arms.push(quote! { #name::#ident(ref s) => ::core::fmt::Display::fmt(s, f) });
}
_ => {
return Err(syn::Error::new_spanned(
@@ -45,7 +58,36 @@
}
}
} else {
- arms.push(quote! { #name::#ident #params => f.pad(#output) });
+ let arm = if let Fields::Named(ref field_names) = variant.fields {
+ let used_vars = capture_format_string_idents(&output)?;
+ if used_vars.is_empty() {
+ quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
+ } else {
+ // Create args like 'name = name, age = age' for format macro
+ let args: Punctuated<_, Token!(,)> = field_names
+ .named
+ .iter()
+ .filter_map(|field| {
+ let ident = field.ident.as_ref().unwrap();
+ // Only contain variables that are used in format string
+ if !used_vars.contains(ident) {
+ None
+ } else {
+ Some(quote! { #ident = #ident })
+ }
+ })
+ .collect();
+
+ quote! {
+ #[allow(unused_variables)]
+ #name::#ident #params => ::core::fmt::Display::fmt(&format!(#output, #args), f)
+ }
+ }
+ } else {
+ quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) }
+ };
+
+ arms.push(arm);
}
}
@@ -63,3 +105,43 @@
}
})
}
+
+fn capture_format_string_idents(string_literal: &LitStr) -> syn::Result<Vec<Ident>> {
+ // Remove escaped brackets
+ let format_str = string_literal.value().replace("{{", "").replace("}}", "");
+
+ let mut new_var_start_index: Option<usize> = None;
+ let mut var_used: Vec<Ident> = Vec::new();
+
+ for (i, chr) in format_str.bytes().enumerate() {
+ if chr == b'{' {
+ if new_var_start_index.is_some() {
+ return Err(syn::Error::new_spanned(
+ string_literal,
+ "Bracket opened without closing previous bracket",
+ ));
+ }
+ new_var_start_index = Some(i);
+ continue;
+ }
+
+ if chr == b'}' {
+ let start_index = new_var_start_index.take().ok_or(syn::Error::new_spanned(
+ string_literal,
+ "Bracket closed without previous opened bracket",
+ ))?;
+
+ let inside_brackets = &format_str[start_index + 1..i];
+ let ident_str = inside_brackets.split(":").next().unwrap();
+ let ident = syn::parse_str::<Ident>(ident_str).map_err(|_| {
+ syn::Error::new_spanned(
+ string_literal,
+ "Invalid identifier inside format string bracket",
+ )
+ })?;
+ var_used.push(ident);
+ }
+ }
+
+ Ok(var_used)
+}
diff --git a/crates/strum_macros/src/macros/strings/from_string.rs b/crates/strum_macros/src/macros/strings/from_string.rs
index 2d25591..de32cbb 100644
--- a/crates/strum_macros/src/macros/strings/from_string.rs
+++ b/crates/strum_macros/src/macros/strings/from_string.rs
@@ -3,7 +3,8 @@
use syn::{Data, DeriveInput, Fields};
use crate::helpers::{
- non_enum_error, occurrence_error, HasStrumVariantProperties, HasTypeProperties,
+ non_enum_error, occurrence_error, HasInnerVariantProperties, HasStrumVariantProperties,
+ HasTypeProperties,
};
pub fn from_string_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
@@ -45,7 +46,6 @@
))
}
}
-
default_kw = Some(kw);
default = quote! {
::core::result::Result::Ok(#name::#ident(s.into()))
@@ -56,16 +56,34 @@
let params = match &variant.fields {
Fields::Unit => quote! {},
Fields::Unnamed(fields) => {
- let defaults =
- ::core::iter::repeat(quote!(Default::default())).take(fields.unnamed.len());
- quote! { (#(#defaults),*) }
+ if let Some(ref value) = variant_properties.default_with {
+ let func = proc_macro2::Ident::new(&value.value(), value.span());
+ let defaults = vec![quote! { #func() }];
+ quote! { (#(#defaults),*) }
+ } else {
+ let defaults =
+ ::core::iter::repeat(quote!(Default::default())).take(fields.unnamed.len());
+ quote! { (#(#defaults),*) }
+ }
}
Fields::Named(fields) => {
- let fields = fields
- .named
- .iter()
- .map(|field| field.ident.as_ref().unwrap());
- quote! { {#(#fields: Default::default()),*} }
+ let mut defaults = vec![];
+ for field in &fields.named {
+ let meta = field.get_variant_inner_properties()?;
+ let field = field.ident.as_ref().unwrap();
+
+ if let Some(default_with) = meta.default_with {
+ let func =
+ proc_macro2::Ident::new(&default_with.value(), default_with.span());
+ defaults.push(quote! {
+ #field: #func()
+ });
+ } else {
+ defaults.push(quote! { #field: Default::default() });
+ }
+ }
+
+ quote! { {#(#defaults),*} }
}
};
@@ -79,7 +97,7 @@
phf_exact_match_arms.push(quote! { #serialization => #name::#ident #params, });
if is_ascii_case_insensitive {
- // Store the lowercase and UPPERCASE variants in the phf map to capture
+ // Store the lowercase and UPPERCASE variants in the phf map to capture
let ser_string = serialization.value();
let lower =
@@ -113,6 +131,7 @@
}
}
};
+
let standard_match_body = if standard_match_arms.is_empty() {
default
} else {
@@ -134,7 +153,6 @@
}
}
};
-
let try_from_str = try_from_str(
name,
&impl_generics,
diff --git a/crates/strum_macros/src/macros/strings/to_string.rs b/crates/strum_macros/src/macros/strings/to_string.rs
index 9a1e661..6d44068 100644
--- a/crates/strum_macros/src/macros/strings/to_string.rs
+++ b/crates/strum_macros/src/macros/strings/to_string.rs
@@ -39,7 +39,8 @@
}
// Look at all the serialize attributes.
- let output = variant_properties.get_preferred_name(type_properties.case_style);
+ let output = variant_properties
+ .get_preferred_name(type_properties.case_style, type_properties.prefix.as_ref());
let params = match variant.fields {
Fields::Unit => quote! {},
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 8b5def0..2455d64 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -4962,9 +4962,9 @@
[[package]]
name = "strum_macros"
-version = "0.25.3"
+version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
dependencies = [
"heck",
"proc-macro2 1.0.92",
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index 6484aad..f680faf 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -310,7 +310,7 @@
static_assertions = "=1.1.0"
strsim = "=0.11.1"
strum = "=0.25.0"
-strum_macros = "=0.25.3"
+strum_macros = "=0.26.2"
syn = "=2.0.90"
syn-mid = "=0.6.0"
sync_wrapper = "=1.0.1"