├── .github └── workflows │ └── ci.yml ├── .gitignore ├── COPYING ├── Cargo.toml ├── README.md ├── core ├── COPYING ├── Cargo.toml ├── lib.rs ├── parse_attributes.rs ├── parse_helpers.rs ├── parse_meta.rs ├── small_string.rs ├── util.rs ├── validations.rs └── with.rs ├── macros ├── COPYING ├── Cargo.toml ├── lib.rs ├── parse_attributes.rs ├── parse_meta_item.rs ├── types.rs ├── types │ ├── enum.rs │ ├── field.rs │ ├── struct.rs │ └── variant.rs └── util.rs ├── src └── lib.rs └── tests ├── attributes.rs ├── meta.rs └── test_util └── mod.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | env: 10 | RUSTFLAGS: -Dwarnings 11 | 12 | jobs: 13 | build_and_test: 14 | name: Deluxe 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 45 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions-rs/toolchain@v1 20 | with: 21 | toolchain: stable 22 | override: true 23 | - run: cargo build -r --all-features --workspace 24 | - run: cargo test -r --all-features --workspace 25 | 26 | build_and_test_no_default: 27 | name: Deluxe No Default Features 28 | runs-on: ubuntu-latest 29 | timeout-minutes: 45 30 | steps: 31 | - uses: actions/checkout@v3 32 | - uses: actions-rs/toolchain@v1 33 | with: 34 | toolchain: stable 35 | override: true 36 | - run: cargo build -r --no-default-features --workspace 37 | - run: cargo test -r --no-default-features --workspace 38 | 39 | rustfmt: 40 | name: Deluxe Rustfmt 41 | runs-on: ubuntu-latest 42 | timeout-minutes: 45 43 | steps: 44 | - uses: actions/checkout@v3 45 | - uses: actions-rs/toolchain@v1 46 | with: 47 | profile: minimal 48 | toolchain: stable 49 | override: true 50 | components: rustfmt 51 | - uses: actions-rs/cargo@v1 52 | with: 53 | command: fmt 54 | args: --all -- --check 55 | 56 | clippy: 57 | name: Deluxe Clippy 58 | runs-on: ubuntu-latest 59 | timeout-minutes: 45 60 | steps: 61 | - uses: actions/checkout@v3 62 | - uses: actions-rs/toolchain@v1 63 | with: 64 | profile: minimal 65 | toolchain: stable 66 | override: true 67 | components: clippy 68 | - uses: actions-rs/cargo@v1 69 | with: 70 | command: clippy 71 | args: --workspace 72 | 73 | docs: 74 | name: Deluxe Docs 75 | runs-on: ubuntu-latest 76 | timeout-minutes: 45 77 | steps: 78 | - uses: actions/checkout@v3 79 | - uses: actions-rs/toolchain@v1 80 | with: 81 | profile: minimal 82 | toolchain: stable 83 | override: true 84 | components: rust-docs 85 | - uses: actions-rs/cargo@v1 86 | with: 87 | command: doc 88 | args: --workspace 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deluxe" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "Procedural macro attribute parser" 6 | license = "MIT" 7 | documentation = "https://docs.rs/deluxe" 8 | homepage = "https://github.com/jf2048/deluxe" 9 | repository = "https://github.com/jf2048/deluxe.git" 10 | readme = "README.md" 11 | keywords = ["macros", "derive", "attributes"] 12 | include = ["/src", "/tests", "/README.md", "/COPYING"] 13 | resolver = "2" 14 | 15 | [features] 16 | default = ["full", "proc-macro"] 17 | full = ["deluxe-core/full", "syn/full"] 18 | proc-macro = ["deluxe-core/proc-macro", "syn/proc-macro"] 19 | 20 | [dependencies] 21 | once_cell = "1.13.0" 22 | proc-macro2 = "1.0.38" 23 | syn = { version = "2.0.10", default-features = false } 24 | deluxe-core = { path = "./core", version = "0.5.0", default-features = false } 25 | deluxe-macros = { path = "./macros", version = "0.5.0" } 26 | 27 | [dev-dependencies] 28 | quote = "1.0.25" 29 | deluxe-core = { path = "./core", version = "0.5.0", features = ["full"], default-features = false } 30 | 31 | [workspace] 32 | members = ["core", "macros"] 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deluxe   [![Latest Version]][crates.io] [![Documentation]][docs] [![Build Status]][actions] 2 | 3 | [Documentation]: https://docs.rs/deluxe/badge.svg 4 | [docs]: https://docs.rs/deluxe 5 | [Build Status]: https://github.com/jf2048/deluxe/workflows/ci/badge.svg?branch=main 6 | [actions]: https://github.com/jf2048/deluxe/actions?query=branch%3Amain 7 | [Latest Version]: https://img.shields.io/crates/v/deluxe.svg 8 | [crates.io]: https://crates.io/crates/deluxe 9 | 10 | A Rust procedural macro attribute parser. 11 | 12 | ### Abstract 13 | 14 | This crate offers attribute parsing closer to the design of attributes in C#. 15 | It has an interface similar to [serde](https://serde.rs). Attributes are 16 | written as plain Rust structs or enums, and then parsers for them are generated 17 | automatically. They can contain arbitrary expressions and can inherit from 18 | other attributes using a flattening mechanism. 19 | 20 | The parsers in this crate directly parse token streams using 21 | [`syn`](https://docs.rs/syn). As a result, most built-in Rust types and `syn` 22 | types can be used directly as fields. 23 | 24 | ### Details 25 | 26 | Functionality in this crate is centered around three traits, and their 27 | respective derive macros: 28 | 29 | - **[`ExtractAttributes`]** 30 | 31 | Extracts attributes from an object containing a list of `syn::Attribute`, and 32 | parses them into a Rust type. Should be implemented for top-level structures 33 | that will be parsed directly out of a set of matching attributes. 34 | - **[`ParseAttributes`]** 35 | 36 | Parses a Rust type from any object containing a list of `syn::Attribute`. 37 | Should be used if the set of matching attributes can potentially be shared 38 | between this type and other types. 39 | - **[`ParseMetaItem`]** 40 | 41 | Parses a Rust type from a `syn::parse::ParseStream`. Should be implemented for 42 | any types that can be nested inside an attribute. 43 | 44 | Basic usage of this crate in derive macros requires simply deriving one (or a 45 | few) of these traits, and then calling [`extract_attributes`] or 46 | [`parse_attributes`]. For more advanced functionality, several `#[deluxe(...)]` 47 | attributes are supported on structs, enums, variants and fields. See the 48 | examples below, and the documentation for each derive macro for a complete 49 | description of the supported attributes. 50 | 51 | A list of field types supported by default can be seen in the list of provided 52 | [`ParseMetaItem` 53 | implementations](https://docs.rs/deluxe-core/latest/deluxe_core/trait.ParseMetaItem.html#foreign-impls). 54 | For more complex usage, manual implementations of these traits can be provided. 55 | See the documentation on the individual traits for more details on how to 56 | manually implement your own parsers. 57 | 58 | ### Related Crates 59 | 60 | Deluxe takes inspiration from the [darling](https://docs.rs/darling) crate, but 61 | offers a few enhancements over it. Darling is built around pre-parsed 62 | `syn::Meta` objects, and therefore is restricted to the [meta 63 | syntax](https://doc.rust-lang.org/stable/reference/attributes.html#meta-item-attribute-syntax). 64 | Deluxe parses its types directly from `TokenStream` objects in the attributes 65 | and so is able to use any syntax that parses as a valid token tree. Deluxe also 66 | does not provide extra traits for parsing special `syn` objects like 67 | `DeriveInput` and `Field`. Instead, Deluxe uses a generic trait to parse from 68 | any type containing a `Vec`. 69 | 70 | ### Examples 71 | 72 | #### Basic Derive Macro 73 | 74 | To create a derive macro that can add some simple metadata to a Rust type from 75 | an attribute, start by defining a struct that derives [`ExtractAttributes`]. 76 | Then, call [`extract_attributes`] in your derive macro to create an instance of 77 | the struct: 78 | 79 | ```rust 80 | #[derive(deluxe::ExtractAttributes)] 81 | #[deluxe(attributes(my_desc))] // Match only `my_desc` attributes 82 | struct MyDescription { 83 | name: String, 84 | version: String, 85 | } 86 | 87 | #[proc_macro_derive(MyDescription, attributes(my_desc))] 88 | pub fn derive_my_description(item: TokenStream) -> TokenStream { 89 | let mut input = syn::parse::(item).unwrap(); 90 | 91 | // Extract a description, modifying `input.attrs` to remove the matched attributes. 92 | let MyDescription { name, version } = match deluxe::extract_attributes(&mut input) { 93 | Ok(desc) => desc, 94 | Err(e) => return e.into_compile_error().into() 95 | }; 96 | 97 | let ident = &input.ident; 98 | let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); 99 | 100 | let tokens = quote::quote! { 101 | impl #impl_generics #ident #type_generics #where_clause { 102 | fn my_desc() -> &'static str { 103 | concat!("Name: ", #name, ", Version: ", #version) 104 | } 105 | } 106 | }; 107 | tokens.into() 108 | } 109 | ``` 110 | 111 | Then, try adding the attribute in some code that uses your macro: 112 | 113 | ```rust 114 | #[derive(MyDescription)] 115 | #[my_desc(name = "hello world", version = "0.2")] 116 | struct Hello(String); 117 | 118 | let hello = Hello("Moon".into()); 119 | assert_eq!(hello.my_desc(), "Name: hello world, Version: 0.2"); 120 | ``` 121 | 122 | #### Basic Attribute Macro 123 | 124 | The [`parse`] and [`parse2`] functions included in this crate can also be used 125 | as simple helpers for attribute macros: 126 | 127 | ```rust 128 | #[derive(deluxe::ParseMetaItem)] 129 | struct MyDescription { 130 | name: String, 131 | version: String, 132 | } 133 | 134 | #[proc_macro_attribute] 135 | pub fn my_desc( 136 | attr: proc_macro::TokenStream, 137 | item: proc_macro::TokenStream, 138 | ) -> proc_macro::TokenStream { 139 | let MyDescription { name, version } = match deluxe::parse::(attr) { 140 | Ok(desc) => desc, 141 | Err(e) => return e.into_compile_error().into() 142 | }; 143 | 144 | let tokens = quote::quote! { 145 | fn my_desc() -> &'static str { 146 | concat!("Name: ", #name, ", Version: ", #version) 147 | } 148 | #item 149 | }; 150 | tokens.into() 151 | } 152 | ``` 153 | 154 | ```rust 155 | // In your normal code 156 | 157 | #[my_desc(name = "hello world", version = "0.2")] 158 | fn nothing() {} 159 | 160 | assert_eq!(my_desc(), "Name: hello world, Version: 0.2"); 161 | ``` 162 | 163 | #### Field Attributes 164 | 165 | The attributes `alias`, `default`, `rename`, and `skip` are supported, and 166 | behave the same as in Serde. The `append` attribute can be used 167 | on `Vec` fields to aggregate all duplicates of a key. The `rest` attribute can 168 | be used to do custom processing on any unknown keys. 169 | 170 | ```rust 171 | #[derive(deluxe::ExtractAttributes)] 172 | #[deluxe(attributes(my_object))] 173 | struct MyObject { 174 | // Can be specified with key `id` or `object_id` 175 | #[deluxe(alias = object_id)] 176 | id: u64, 177 | 178 | // Field is optional, defaults to `Default::default` if not present 179 | #[deluxe(default)] 180 | count: u64, 181 | 182 | // Defaults to "Empty" if not present 183 | #[deluxe(default = String::from("Empty"))] 184 | contents: String, 185 | 186 | // Can be specified only with key `name` 187 | #[deluxe(rename = name)] 188 | s: String, 189 | 190 | // Skipped during parsing entirely 191 | #[deluxe(skip)] 192 | internal_flag: bool, 193 | 194 | // Appends any extra fields with the key `expr` to the Vec 195 | #[deluxe(append, rename = expr)] 196 | exprs: Vec, 197 | 198 | // Adds any unknown keys to the hash map 199 | #[deluxe(rest)] 200 | rest: std::collections::HashMap, 201 | } 202 | ``` 203 | 204 | ```rust 205 | // Omitted fields will be set to defaults 206 | #[derive(MyObject)] 207 | #[my_object(id = 1, name = "First", expr = 1 + 2, count = 3)] 208 | struct FirstObject; 209 | 210 | // `expr` can be specified multiple times because of the `append` attribute 211 | #[derive(MyObject)] 212 | #[my_object(object_id = 2, name = "Second", expr = 1 + 2, expr = 3 + 4)] 213 | struct SecondObject; 214 | 215 | // `unknown` and `extra` will be stored in the `rest` hashmap 216 | #[derive(MyObject)] 217 | #[my_object(id = 3, name = "Third", unknown = 1 + 2, extra = 3 + 4)] 218 | struct ThirdObject; 219 | ``` 220 | 221 | #### Inheritance 222 | 223 | The `flatten` attribute can be used to parse keys from one structure inside another: 224 | 225 | ```rust 226 | #[derive(deluxe::ParseMetaItem)] 227 | struct A { 228 | id: u64, 229 | } 230 | 231 | #[derive(deluxe::ExtractAttributes)] 232 | #[deluxe(attributes(b))] 233 | struct B { 234 | #[deluxe(flatten)] 235 | a: A, 236 | name: String, 237 | } 238 | ``` 239 | 240 | Then, fields from both `A` and `B` can be used when deriving `B`: 241 | 242 | ```rust 243 | #[derive(B)] 244 | #[b(id = 123, name = "object")] 245 | struct Object; 246 | ``` 247 | 248 | #### Attributes in Nested Code 249 | 250 | Extra attributes can be taken from within the code block attached to a macro. 251 | When used in an attribute macro, the attributes should be consumed so as not to 252 | produce an "unknown attribute" error when outputting tokens. 253 | 254 | ```rust 255 | #[derive(Default, deluxe::ParseMetaItem, deluxe::ExtractAttributes)] 256 | struct MyDescription { 257 | name: String, 258 | version: String, 259 | } 260 | 261 | #[derive(deluxe::ExtractAttributes)] 262 | #[deluxe(attributes(author))] 263 | struct Authors(#[deluxe(flatten)] Vec); 264 | 265 | #[proc_macro_derive(MyDescription, attributes(my_desc))] 266 | pub fn derive_my_description(item: TokenStream) -> TokenStream { 267 | let mut input = syn::parse::(item).unwrap(); 268 | 269 | // Parsing functions suffixed with `_optional` can be used to continue 270 | // parsing after an error. Any errors will get accumulated into an `Errors` 271 | // structure, which can then be manually included in the token output to 272 | // produce compile errors. 273 | let errors = deluxe::Errors::new(); 274 | let MyDescription { name, version } = deluxe::extract_attributes_optional(&mut input, &errors); 275 | 276 | let mut authors = Vec::new(); 277 | if let syn::Data::Struct(s) = &mut input.data { 278 | // Look through all fields in the struct for `author` attributes 279 | for field in s.fields.iter_mut() { 280 | // Aggregate any errors to avoid exiting the loop early 281 | match deluxe::extract_attributes(field) { 282 | Ok(Authors(a)) => authors.extend(a), 283 | Err(e) => errors.push_syn(e), 284 | } 285 | } 286 | } 287 | 288 | let ident = &input.ident; 289 | let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); 290 | 291 | // Make sure to include the errors in the output 292 | let tokens = quote::quote! { 293 | #errors 294 | impl #impl_generics #ident #type_generics #where_clause { 295 | fn my_desc() -> &'static str { 296 | concat!("Name: ", #name, ", Version: ", #version #(, ", Author: ", #authors)*) 297 | } 298 | } 299 | }; 300 | tokens.into() 301 | } 302 | 303 | #[proc_macro_attribute] 304 | pub fn my_desc_mod( 305 | attr: proc_macro::TokenStream, 306 | item: proc_macro::TokenStream, 307 | ) -> proc_macro::TokenStream { 308 | let mut module = syn::parse::(item) { 309 | Ok(module) => module, 310 | Err(e) => return e.into_compile_error().into() 311 | }; 312 | 313 | let errors = deluxe::Errors::new(); 314 | let MyDescription { name, version } = deluxe::parse_optional(attr, &errors); 315 | 316 | let (_, items) = module.content.as_mut().unwrap(); 317 | 318 | let mut authors = Vec::new(); 319 | // Look through all items in the module for `author` attributes 320 | for i in items.iter_mut() { 321 | // Extract the attributes to remove them from the final output 322 | match deluxe::extract_attributes(i) { 323 | Ok(Authors(a)) => authors.extend(a), 324 | Err(e) => errors.push_syn(e), 325 | } 326 | } 327 | 328 | // Place a new function inside the module 329 | items.push(syn::parse_quote! { 330 | fn my_desc() -> &'static str { 331 | concat!("Name: ", #name, ", Version: ", #version #(, ", Author: ", #authors)*) 332 | } 333 | }); 334 | 335 | // Make sure to include the errors in the output 336 | let tokens = quote::quote! { #module #errors }; 337 | tokens.into() 338 | } 339 | ``` 340 | 341 | ```rust 342 | // In your normal code 343 | 344 | #[derive(MyDescription, Default)] 345 | #[my_desc(name = "hello world", version = "0.2")] 346 | struct Hello { 347 | #[author("Alice")] 348 | a: i32, 349 | #[author("Bob")] 350 | b: String 351 | } 352 | 353 | let hello: Hello = Default::default(); 354 | assert_eq!(hello.my_desc(), "Name: hello world, Version: 0.2, Author: Alice, Author: Bob"); 355 | 356 | #[my_desc_mod(name = "hello world", version = "0.2")] 357 | mod abc { 358 | #[author("Alice", "Bob")] 359 | fn func1() {} 360 | 361 | #[author("Carol")] 362 | #[author("Dave")] 363 | fn func2() {} 364 | } 365 | 366 | assert_eq!( 367 | abc::my_desc(), 368 | "Name: hello world, Version: 0.2, Author: Alice, Author: Bob, Author: Carol, Author: Dave" 369 | ); 370 | ``` 371 | 372 | #### Tuple Structs, Tuples and Vecs 373 | 374 | Deluxe also supports parsing into data structures with unnamed fields. 375 | 376 | ```rust 377 | #[derive(deluxe::ExtractAttributes)] 378 | #[deluxe(attributes(my_tuple))] 379 | struct MyTuple(u64, String); 380 | 381 | #[derive(deluxe::ExtractAttributes)] 382 | #[deluxe(attributes(my_idents))] 383 | struct MyIdents { 384 | id: u64, 385 | names: (String, String), 386 | idents: Vec 387 | } 388 | ``` 389 | 390 | The standard attribute syntax with parenthesis can be used when specifying a 391 | `Vec` type. The alternative syntax `key = [...]` can also be used to have an 392 | appearance similar to an array literal. 393 | 394 | ```rust 395 | #[derive(MyTuple)] 396 | #[my_tuple(123, "object")] 397 | struct Object; 398 | 399 | #[derive(MyIdents)] 400 | #[my_idents(id = 7, names("hello", "world"), idents(a, b, c))] 401 | struct ABC; 402 | 403 | // `idents` contains same values as above 404 | #[derive(MyIdents)] 405 | #[my_idents(id = 7, names("hello", "world"), idents = [a, b, c])] 406 | struct ABC2; 407 | ``` 408 | 409 | #### C#-styled Attributes 410 | 411 | Attributes in C# can support positional arguments first with the named 412 | arguments afterwards. This style can be emulated by using a tuple struct with a 413 | normal struct flattened at the end. Placing `#[deluxe(default)]` on the struct 414 | behaves the same as Serde, by filling in all fields with values from `Default`, 415 | allowing every named argument to be optional. 416 | 417 | ```rust 418 | #[derive(deluxe::ParseMetaItem, Default)] 419 | #[deluxe(default)] 420 | struct Flags { 421 | native: bool, 422 | } 423 | 424 | #[derive(deluxe::ExtractAttributes)] 425 | #[deluxe(attributes(a))] 426 | struct A(u64, String, #[deluxe(flatten)] Flags); 427 | ``` 428 | 429 | ```rust 430 | #[derive(A)] 431 | #[a(123, "object")] 432 | struct Object; 433 | 434 | #[derive(A)] 435 | #[a(123, "native-object", native = true)] 436 | struct NativeObject; 437 | ``` 438 | 439 | #### Enums 440 | 441 | Enums are supported by using the variant name as a single key, in snake-case. 442 | Variants can be renamed, aliased and skipped in the same way as fields. 443 | 444 | ```rust 445 | #[derive(deluxe::ExtractAttributes)] 446 | #[deluxe(attributes(my_enum))] 447 | enum MyEnum { 448 | A, 449 | B, 450 | C, 451 | #[deluxe(alias = d)] 452 | AnotherOne, 453 | #[deluxe(rename = e)] 454 | AnotherTwo, 455 | #[deluxe(skip)] 456 | SkipMe 457 | } 458 | ``` 459 | 460 | ```rust 461 | #[derive(MyEnum)] 462 | #[my_enum(b)] 463 | struct ObjectB; 464 | 465 | #[derive(MyEnum)] 466 | #[my_enum(another_one)] 467 | struct ObjectD; 468 | ``` 469 | 470 | #### Complex Enums 471 | 472 | Enums with struct and tuple variants are also supported. The data inside is 473 | used as arguments to the attribute. All field attributes from structs are also 474 | supported inside variants. 475 | 476 | Additionally, enum variants with named fields can be flattened. The behavior of 477 | a flattened variant is similar to Serde's `untagged` mode. In a flattened 478 | variant, the name of the variant will be ignored. Instead, Deluxe will attempt 479 | to use the unique keys in each variant to determine if that variant was 480 | specified. A compile error will be thrown if it is not possible to determine a 481 | unique, unambiguous key between two variants. 482 | 483 | ```rust 484 | #[derive(deluxe::ExtractAttributes)] 485 | #[deluxe(attributes(my_enum))] 486 | enum MyEnum { 487 | A, 488 | B(u64, String), 489 | C { id: u64, name: String }, 490 | #[deluxe(flatten)] 491 | D { d: u64, name: String }, 492 | } 493 | ``` 494 | 495 | ```rust 496 | #[derive(MyEnum)] 497 | #[my_enum(a)] 498 | struct ObjectA; 499 | 500 | #[derive(MyEnum)] 501 | #[my_enum(b(1, "hello"))] 502 | struct ObjectB; 503 | 504 | #[derive(MyEnum)] 505 | #[my_enum(c(id = 2, name = "world"))] 506 | struct ObjectC; 507 | 508 | // No inner parenthesis needed here due to flattening 509 | #[derive(MyEnum)] 510 | #[my_enum(d = 3, name = "moon")] 511 | struct ObjectD; 512 | ``` 513 | 514 | #### Storing Containers 515 | 516 | During parsing, Deluxe can store references to the container type holding the 517 | attributes for easier access. Container fields are skipped during attribute 518 | parsing. 519 | 520 | ```rust 521 | #[derive(deluxe::ParseAttributes)] 522 | #[deluxe(attributes(my_object))] 523 | struct MyObject<'t> { 524 | id: u64, 525 | // Fill `container` in using the parsed type. Note this restricts the 526 | // derived `ParseAttributes` impl so it can only be used on `DeriveInput`. 527 | #[deluxe(container)] 528 | container: &'t syn::DeriveInput, 529 | } 530 | 531 | #[proc_macro_derive(MyObject, attributes(my_desc))] 532 | pub fn derive_my_object(item: TokenStream) -> TokenStream { 533 | let input = syn::parse::(item).unwrap(); 534 | 535 | // `obj.container` now holds a reference to `input` 536 | let obj: MyObject = match deluxe::parse_attributes(&input) { 537 | Ok(obj) => obj, 538 | Err(e) => return e.into_compile_error().into() 539 | }; 540 | 541 | let tokens = quote::quote! { /* ... generate some code here ... */ }; 542 | 543 | tokens.into() 544 | } 545 | ``` 546 | 547 | To support both extracting and parsing, a container field can also be a value 548 | type. In that case, the container will be cloned into the structure. 549 | 550 | [`ParseMetaItem`]: (https://docs.rs/deluxe/latest/deluxe/derive.ParseMetaItem.html) 551 | [`ParseAttributes`]: (https://docs.rs/deluxe/latest/deluxe/derive.ParseAttributes.html) 552 | [`ExtractAttributes`]: (https://docs.rs/deluxe/latest/deluxe/derive.ExtractAttributes.html) 553 | [`parse_attributes`]: (https://docs.rs/deluxe/latest/deluxe/fn.parse_attributes.html) 554 | [`extract_attributes`]: (https://docs.rs/deluxe/latest/deluxe/fn.extract_attributes.html) 555 | [`parse`]: (https://docs.rs/deluxe/latest/deluxe/fn.parse.html) 556 | [`parse2`]: (https://docs.rs/deluxe/latest/deluxe/fn.parse2.html) 557 | -------------------------------------------------------------------------------- /core/COPYING: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deluxe-core" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "Core traits and helpers for Deluxe procedural macro attribute parser" 6 | license = "MIT" 7 | documentation = "https://docs.rs/deluxe-core" 8 | homepage = "https://github.com/jf2048/deluxe" 9 | repository = "https://github.com/jf2048/deluxe.git" 10 | 11 | [lib] 12 | path = "lib.rs" 13 | 14 | [features] 15 | default = ["full", "proc-macro"] 16 | full = ["syn/full"] 17 | proc-macro = ["syn/proc-macro"] 18 | 19 | [dependencies] 20 | arrayvec = "0.7.2" 21 | proc-macro2 = "1.0.38" 22 | quote = "1.0.25" 23 | strsim = "0.10.0" 24 | syn = { version = "2.0.10", features = ["clone-impls", "derive", "extra-traits", "parsing", "printing"], default-features = false } 25 | -------------------------------------------------------------------------------- /core/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Deluxe Core 2 | //! 3 | //! Core functions and traits shared between [`deluxe`](https://docs.rs/deluxe) and 4 | //! [`deluxe_macros`](https://docs.rs/deluxe-macros). 5 | //! 6 | //! This crate is used by [`deluxe_macros`](https://docs.rs/deluxe-macros) to parse its own 7 | //! attributes. Code generated by its derive macros also references items from this crate 8 | //! re-exported into [`deluxe`](https://docs.rs/deluxe). The functions in [`parse_helpers`] are used 9 | //! internally by the derive macros, but can also be used for convenience when manually 10 | //! implementing any of the parsing traits. 11 | //! 12 | //! See the documentation for the [`deluxe`](https://docs.rs/deluxe) crate for a high-level overview 13 | //! of how Deluxe works. 14 | 15 | #![deny(missing_docs)] 16 | #![deny(unsafe_code)] 17 | 18 | #[cfg(feature = "proc-macro")] 19 | extern crate proc_macro; 20 | 21 | mod parse_attributes; 22 | pub mod parse_helpers; 23 | mod parse_meta; 24 | mod small_string; 25 | mod util; 26 | pub mod validations; 27 | pub mod with; 28 | 29 | pub use parse_attributes::*; 30 | pub use parse_meta::*; 31 | pub use util::*; 32 | 33 | pub use proc_macro2::Span; 34 | 35 | #[doc(hidden)] 36 | pub use syn; 37 | #[doc(hidden)] 38 | pub use { 39 | std::{ 40 | borrow::Borrow, 41 | collections::HashMap, 42 | fmt, 43 | hash::{Hash, Hasher}, 44 | ops, primitive, stringify, 45 | }, 46 | AsRef, Clone, Default, Eq, IntoIterator, Iterator, Option, PartialEq, 47 | }; 48 | -------------------------------------------------------------------------------- /core/parse_attributes.rs: -------------------------------------------------------------------------------- 1 | use crate::Result; 2 | use proc_macro2::Span; 3 | use syn::spanned::Spanned; 4 | 5 | /// Parses a structure out of a [`syn::Attribute`] list. 6 | /// 7 | /// This trait is intended for types that may share a set of matching attributes with other types. 8 | pub trait ParseAttributes<'t, T: HasAttributes>: Sized { 9 | /// Checks if a given attribute path can be parsed by this type. 10 | fn path_matches(path: &syn::Path) -> bool; 11 | /// Iterates the attributes from a `T`, parsing any that match. 12 | /// 13 | /// Implementations should use the [`ref_tokens`](crate::parse_helpers::ref_tokens) helper to 14 | /// automatically get references to any matching `TokenStream`s, and then parse them using 15 | /// [`parse_struct_attr_tokens`](crate::parse_helpers::parse_struct_attr_tokens). 16 | fn parse_attributes(obj: &'t T) -> Result; 17 | } 18 | 19 | /// Extracts a structure out of a [`syn::Attribute`] list. 20 | /// 21 | /// This trait is intended for types that "consume" attributes from another syntax tree node. 22 | pub trait ExtractAttributes: Sized { 23 | /// Checks if a given attribute path can be extracted by this type. 24 | fn path_matches(path: &syn::Path) -> bool; 25 | /// Iterates the attributes from a `T`, extracting and parsing any that match. 26 | /// 27 | /// Implementations should use the [`take_tokens`](crate::parse_helpers::take_tokens) helper to 28 | /// automatically extract any matching `TokenStream`s, and then parse them using 29 | /// [`parse_struct_attr_tokens`](crate::parse_helpers::parse_struct_attr_tokens). 30 | fn extract_attributes(obj: &mut T) -> Result; 31 | } 32 | 33 | /// Trait for a [`syn`] type containing a list of attributes. 34 | /// 35 | /// Implementations are provided for all [`syn`] types containing a 36 | /// [Vec]<[syn::Attribute]>. 37 | pub trait HasAttributes { 38 | /// Returns an immutable slice of attributes. 39 | fn attrs(&self) -> &[syn::Attribute]; 40 | /// Returns a mutable [`Vec`] of attributes. 41 | /// 42 | /// Returns [`Err`] if the type does not support mutable attributes. 43 | fn attrs_mut(&mut self) -> Result<&mut Vec>; 44 | } 45 | 46 | impl HasAttributes for syn::Attribute { 47 | #[inline] 48 | fn attrs(&self) -> &[syn::Attribute] { 49 | std::slice::from_ref(self) 50 | } 51 | #[inline] 52 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 53 | Err(syn::Error::new( 54 | self.span(), 55 | "`HasAttributes::attrs_mut()` not supported on single `syn::Attribute`, try moving it into a `Vec`" 56 | )) 57 | } 58 | } 59 | 60 | impl HasAttributes for [syn::Attribute] { 61 | #[inline] 62 | fn attrs(&self) -> &[syn::Attribute] { 63 | self 64 | } 65 | #[inline] 66 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 67 | let span = self 68 | .iter() 69 | .fold(None, |s, a| { 70 | Some(s.and_then(|s| a.span().join(s)).unwrap_or_else(|| a.span())) 71 | }) 72 | .unwrap_or_else(Span::call_site); 73 | Err(syn::Error::new( 74 | span, 75 | "`HasAttributes::attrs_mut()` not supported on immutable `[syn::Attribute]`, try using a `Vec`" 76 | )) 77 | } 78 | } 79 | 80 | impl HasAttributes for Vec { 81 | #[inline] 82 | fn attrs(&self) -> &[syn::Attribute] { 83 | self.as_slice() 84 | } 85 | #[inline] 86 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 87 | Ok(self) 88 | } 89 | } 90 | 91 | macro_rules! impl_has_attributes { 92 | ($(#[$attr:meta])* $ty:ty) => { 93 | $(#[$attr])* 94 | impl HasAttributes for $ty { 95 | #[inline] 96 | fn attrs(&self) -> &[syn::Attribute] { 97 | &self.attrs 98 | } 99 | #[inline] 100 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 101 | Ok(&mut self.attrs) 102 | } 103 | } 104 | }; 105 | ($ty:ty, #full) => { 106 | impl_has_attributes!( 107 | #[cfg(feature = "full")] 108 | #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] 109 | $ty 110 | ); 111 | }; 112 | } 113 | 114 | impl_has_attributes!(syn::Arm, #full); 115 | impl_has_attributes!(syn::BareFnArg); 116 | impl_has_attributes!(syn::ConstParam); 117 | impl_has_attributes!(syn::DeriveInput); 118 | 119 | #[cfg(feature = "full")] 120 | #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] 121 | impl HasAttributes for syn::Expr { 122 | fn attrs(&self) -> &[syn::Attribute] { 123 | match self { 124 | syn::Expr::Array(expr) => &expr.attrs, 125 | syn::Expr::Assign(expr) => &expr.attrs, 126 | syn::Expr::Async(expr) => &expr.attrs, 127 | syn::Expr::Await(expr) => &expr.attrs, 128 | syn::Expr::Binary(expr) => &expr.attrs, 129 | syn::Expr::Block(expr) => &expr.attrs, 130 | syn::Expr::Break(expr) => &expr.attrs, 131 | syn::Expr::Call(expr) => &expr.attrs, 132 | syn::Expr::Cast(expr) => &expr.attrs, 133 | syn::Expr::Closure(expr) => &expr.attrs, 134 | syn::Expr::Const(expr) => &expr.attrs, 135 | syn::Expr::Continue(expr) => &expr.attrs, 136 | syn::Expr::Field(expr) => &expr.attrs, 137 | syn::Expr::ForLoop(expr) => &expr.attrs, 138 | syn::Expr::Group(expr) => &expr.attrs, 139 | syn::Expr::If(expr) => &expr.attrs, 140 | syn::Expr::Index(expr) => &expr.attrs, 141 | syn::Expr::Infer(expr) => &expr.attrs, 142 | syn::Expr::Let(expr) => &expr.attrs, 143 | syn::Expr::Lit(expr) => &expr.attrs, 144 | syn::Expr::Loop(expr) => &expr.attrs, 145 | syn::Expr::Macro(expr) => &expr.attrs, 146 | syn::Expr::Match(expr) => &expr.attrs, 147 | syn::Expr::MethodCall(expr) => &expr.attrs, 148 | syn::Expr::Paren(expr) => &expr.attrs, 149 | syn::Expr::Path(expr) => &expr.attrs, 150 | syn::Expr::Range(expr) => &expr.attrs, 151 | syn::Expr::Reference(expr) => &expr.attrs, 152 | syn::Expr::Repeat(expr) => &expr.attrs, 153 | syn::Expr::Return(expr) => &expr.attrs, 154 | syn::Expr::Struct(expr) => &expr.attrs, 155 | syn::Expr::Try(expr) => &expr.attrs, 156 | syn::Expr::TryBlock(expr) => &expr.attrs, 157 | syn::Expr::Tuple(expr) => &expr.attrs, 158 | syn::Expr::Unary(expr) => &expr.attrs, 159 | syn::Expr::Unsafe(expr) => &expr.attrs, 160 | syn::Expr::While(expr) => &expr.attrs, 161 | syn::Expr::Yield(expr) => &expr.attrs, 162 | _ => &[], 163 | } 164 | } 165 | 166 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 167 | match self { 168 | syn::Expr::Array(expr) => Ok(&mut expr.attrs), 169 | syn::Expr::Assign(expr) => Ok(&mut expr.attrs), 170 | syn::Expr::Async(expr) => Ok(&mut expr.attrs), 171 | syn::Expr::Await(expr) => Ok(&mut expr.attrs), 172 | syn::Expr::Binary(expr) => Ok(&mut expr.attrs), 173 | syn::Expr::Block(expr) => Ok(&mut expr.attrs), 174 | syn::Expr::Break(expr) => Ok(&mut expr.attrs), 175 | syn::Expr::Call(expr) => Ok(&mut expr.attrs), 176 | syn::Expr::Cast(expr) => Ok(&mut expr.attrs), 177 | syn::Expr::Closure(expr) => Ok(&mut expr.attrs), 178 | syn::Expr::Const(expr) => Ok(&mut expr.attrs), 179 | syn::Expr::Continue(expr) => Ok(&mut expr.attrs), 180 | syn::Expr::Field(expr) => Ok(&mut expr.attrs), 181 | syn::Expr::ForLoop(expr) => Ok(&mut expr.attrs), 182 | syn::Expr::Group(expr) => Ok(&mut expr.attrs), 183 | syn::Expr::If(expr) => Ok(&mut expr.attrs), 184 | syn::Expr::Index(expr) => Ok(&mut expr.attrs), 185 | syn::Expr::Let(expr) => Ok(&mut expr.attrs), 186 | syn::Expr::Lit(expr) => Ok(&mut expr.attrs), 187 | syn::Expr::Loop(expr) => Ok(&mut expr.attrs), 188 | syn::Expr::Macro(expr) => Ok(&mut expr.attrs), 189 | syn::Expr::Match(expr) => Ok(&mut expr.attrs), 190 | syn::Expr::MethodCall(expr) => Ok(&mut expr.attrs), 191 | syn::Expr::Paren(expr) => Ok(&mut expr.attrs), 192 | syn::Expr::Path(expr) => Ok(&mut expr.attrs), 193 | syn::Expr::Range(expr) => Ok(&mut expr.attrs), 194 | syn::Expr::Reference(expr) => Ok(&mut expr.attrs), 195 | syn::Expr::Repeat(expr) => Ok(&mut expr.attrs), 196 | syn::Expr::Return(expr) => Ok(&mut expr.attrs), 197 | syn::Expr::Struct(expr) => Ok(&mut expr.attrs), 198 | syn::Expr::Try(expr) => Ok(&mut expr.attrs), 199 | syn::Expr::TryBlock(expr) => Ok(&mut expr.attrs), 200 | syn::Expr::Tuple(expr) => Ok(&mut expr.attrs), 201 | syn::Expr::Unary(expr) => Ok(&mut expr.attrs), 202 | syn::Expr::Unsafe(expr) => Ok(&mut expr.attrs), 203 | syn::Expr::While(expr) => Ok(&mut expr.attrs), 204 | syn::Expr::Yield(expr) => Ok(&mut expr.attrs), 205 | syn::Expr::Verbatim(_) => Err(syn::Error::new_spanned( 206 | self, 207 | "`HasAttributes::attrs_mut` not supported with `syn::Expr::Verbatim`", 208 | )), 209 | _ => Err(syn::Error::new_spanned( 210 | self, 211 | "`HasAttributes::attrs_mut` encountered unknown `syn::Expr` variant", 212 | )), 213 | } 214 | } 215 | } 216 | 217 | impl_has_attributes!(syn::ExprArray, #full); 218 | impl_has_attributes!(syn::ExprAssign, #full); 219 | impl_has_attributes!(syn::ExprAsync, #full); 220 | impl_has_attributes!(syn::ExprAwait, #full); 221 | impl_has_attributes!(syn::ExprBinary, #full); 222 | impl_has_attributes!(syn::ExprBlock, #full); 223 | impl_has_attributes!(syn::ExprBreak, #full); 224 | impl_has_attributes!(syn::ExprCall, #full); 225 | impl_has_attributes!(syn::ExprCast, #full); 226 | impl_has_attributes!(syn::ExprClosure, #full); 227 | impl_has_attributes!(syn::ExprConst, #full); 228 | impl_has_attributes!(syn::ExprContinue, #full); 229 | impl_has_attributes!(syn::ExprField, #full); 230 | impl_has_attributes!(syn::ExprForLoop, #full); 231 | impl_has_attributes!(syn::ExprGroup, #full); 232 | impl_has_attributes!(syn::ExprIf, #full); 233 | impl_has_attributes!(syn::ExprIndex, #full); 234 | impl_has_attributes!(syn::ExprInfer, #full); 235 | impl_has_attributes!(syn::ExprLet, #full); 236 | impl_has_attributes!(syn::ExprLit, #full); 237 | impl_has_attributes!(syn::ExprLoop, #full); 238 | impl_has_attributes!(syn::ExprMacro, #full); 239 | impl_has_attributes!(syn::ExprMatch, #full); 240 | impl_has_attributes!(syn::ExprMethodCall, #full); 241 | impl_has_attributes!(syn::ExprParen, #full); 242 | impl_has_attributes!(syn::ExprPath, #full); 243 | impl_has_attributes!(syn::ExprRange, #full); 244 | impl_has_attributes!(syn::ExprReference, #full); 245 | impl_has_attributes!(syn::ExprRepeat, #full); 246 | impl_has_attributes!(syn::ExprReturn, #full); 247 | impl_has_attributes!(syn::ExprStruct, #full); 248 | impl_has_attributes!(syn::ExprTry, #full); 249 | impl_has_attributes!(syn::ExprTryBlock, #full); 250 | impl_has_attributes!(syn::ExprTuple, #full); 251 | impl_has_attributes!(syn::ExprUnary, #full); 252 | impl_has_attributes!(syn::ExprUnsafe, #full); 253 | impl_has_attributes!(syn::ExprWhile, #full); 254 | impl_has_attributes!(syn::ExprYield, #full); 255 | impl_has_attributes!(syn::Field); 256 | impl_has_attributes!(syn::FieldPat, #full); 257 | impl_has_attributes!(syn::FieldValue, #full); 258 | impl_has_attributes!(syn::File, #full); 259 | 260 | #[cfg(feature = "full")] 261 | #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] 262 | impl HasAttributes for syn::ForeignItem { 263 | fn attrs(&self) -> &[syn::Attribute] { 264 | match self { 265 | syn::ForeignItem::Fn(item) => &item.attrs, 266 | syn::ForeignItem::Static(item) => &item.attrs, 267 | syn::ForeignItem::Type(item) => &item.attrs, 268 | syn::ForeignItem::Macro(item) => &item.attrs, 269 | _ => &[], 270 | } 271 | } 272 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 273 | match self { 274 | syn::ForeignItem::Fn(item) => Ok(&mut item.attrs), 275 | syn::ForeignItem::Static(item) => Ok(&mut item.attrs), 276 | syn::ForeignItem::Type(item) => Ok(&mut item.attrs), 277 | syn::ForeignItem::Macro(item) => Ok(&mut item.attrs), 278 | syn::ForeignItem::Verbatim(_) => Err(syn::Error::new_spanned( 279 | self, 280 | "`HasAttributes::attrs_mut` not supported with `syn::ForeignItem::Verbatim`", 281 | )), 282 | _ => Err(syn::Error::new_spanned( 283 | self, 284 | "`HasAttributes::attrs_mut` encountered unknown `syn::ForeignItem` variant", 285 | )), 286 | } 287 | } 288 | } 289 | 290 | impl_has_attributes!(syn::ForeignItemFn, #full); 291 | impl_has_attributes!(syn::ForeignItemMacro, #full); 292 | impl_has_attributes!(syn::ForeignItemStatic, #full); 293 | impl_has_attributes!(syn::ForeignItemType, #full); 294 | 295 | #[cfg(feature = "full")] 296 | #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] 297 | impl HasAttributes for syn::ImplItem { 298 | fn attrs(&self) -> &[syn::Attribute] { 299 | match self { 300 | syn::ImplItem::Const(item) => &item.attrs, 301 | syn::ImplItem::Fn(item) => &item.attrs, 302 | syn::ImplItem::Type(item) => &item.attrs, 303 | syn::ImplItem::Macro(item) => &item.attrs, 304 | _ => &[], 305 | } 306 | } 307 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 308 | match self { 309 | syn::ImplItem::Const(item) => Ok(&mut item.attrs), 310 | syn::ImplItem::Fn(item) => Ok(&mut item.attrs), 311 | syn::ImplItem::Type(item) => Ok(&mut item.attrs), 312 | syn::ImplItem::Macro(item) => Ok(&mut item.attrs), 313 | syn::ImplItem::Verbatim(_) => Err(syn::Error::new_spanned( 314 | self, 315 | "`HasAttributes::attrs_mut` not supported with `syn::ImplItem::Verbatim`", 316 | )), 317 | _ => Err(syn::Error::new_spanned( 318 | self, 319 | "`HasAttributes::attrs_mut` encountered unknown `syn::ImplItem` variant", 320 | )), 321 | } 322 | } 323 | } 324 | 325 | impl_has_attributes!(syn::ImplItemConst, #full); 326 | impl_has_attributes!(syn::ImplItemFn, #full); 327 | impl_has_attributes!(syn::ImplItemMacro, #full); 328 | impl_has_attributes!(syn::ImplItemType, #full); 329 | 330 | #[cfg(feature = "full")] 331 | #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] 332 | impl HasAttributes for syn::Item { 333 | fn attrs(&self) -> &[syn::Attribute] { 334 | match self { 335 | syn::Item::Const(expr) => &expr.attrs, 336 | syn::Item::Enum(expr) => &expr.attrs, 337 | syn::Item::ExternCrate(expr) => &expr.attrs, 338 | syn::Item::Fn(expr) => &expr.attrs, 339 | syn::Item::ForeignMod(expr) => &expr.attrs, 340 | syn::Item::Impl(expr) => &expr.attrs, 341 | syn::Item::Macro(expr) => &expr.attrs, 342 | syn::Item::Mod(expr) => &expr.attrs, 343 | syn::Item::Static(expr) => &expr.attrs, 344 | syn::Item::Struct(expr) => &expr.attrs, 345 | syn::Item::Trait(expr) => &expr.attrs, 346 | syn::Item::TraitAlias(expr) => &expr.attrs, 347 | syn::Item::Type(expr) => &expr.attrs, 348 | syn::Item::Union(expr) => &expr.attrs, 349 | syn::Item::Use(expr) => &expr.attrs, 350 | _ => &[], 351 | } 352 | } 353 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 354 | match self { 355 | syn::Item::Const(expr) => Ok(&mut expr.attrs), 356 | syn::Item::Enum(expr) => Ok(&mut expr.attrs), 357 | syn::Item::ExternCrate(expr) => Ok(&mut expr.attrs), 358 | syn::Item::Fn(expr) => Ok(&mut expr.attrs), 359 | syn::Item::ForeignMod(expr) => Ok(&mut expr.attrs), 360 | syn::Item::Impl(expr) => Ok(&mut expr.attrs), 361 | syn::Item::Macro(expr) => Ok(&mut expr.attrs), 362 | syn::Item::Mod(expr) => Ok(&mut expr.attrs), 363 | syn::Item::Static(expr) => Ok(&mut expr.attrs), 364 | syn::Item::Struct(expr) => Ok(&mut expr.attrs), 365 | syn::Item::Trait(expr) => Ok(&mut expr.attrs), 366 | syn::Item::TraitAlias(expr) => Ok(&mut expr.attrs), 367 | syn::Item::Type(expr) => Ok(&mut expr.attrs), 368 | syn::Item::Union(expr) => Ok(&mut expr.attrs), 369 | syn::Item::Use(expr) => Ok(&mut expr.attrs), 370 | syn::Item::Verbatim(_) => Err(syn::Error::new_spanned( 371 | self, 372 | "`HasAttributes::attrs_mut` not supported with `syn::Item::Verbatim`", 373 | )), 374 | _ => Err(syn::Error::new_spanned( 375 | self, 376 | "`HasAttributes::attrs_mut` encountered unknown `syn::Item` variant", 377 | )), 378 | } 379 | } 380 | } 381 | 382 | impl_has_attributes!(syn::ItemConst, #full); 383 | impl_has_attributes!(syn::ItemEnum, #full); 384 | impl_has_attributes!(syn::ItemExternCrate, #full); 385 | impl_has_attributes!(syn::ItemFn, #full); 386 | impl_has_attributes!(syn::ItemForeignMod, #full); 387 | impl_has_attributes!(syn::ItemImpl, #full); 388 | impl_has_attributes!(syn::ItemMacro, #full); 389 | impl_has_attributes!(syn::ItemMod, #full); 390 | impl_has_attributes!(syn::ItemStatic, #full); 391 | impl_has_attributes!(syn::ItemStruct, #full); 392 | impl_has_attributes!(syn::ItemTrait, #full); 393 | impl_has_attributes!(syn::ItemTraitAlias, #full); 394 | impl_has_attributes!(syn::ItemType, #full); 395 | impl_has_attributes!(syn::ItemUnion, #full); 396 | impl_has_attributes!(syn::ItemUse, #full); 397 | impl_has_attributes!(syn::LifetimeParam); 398 | impl_has_attributes!(syn::Local, #full); 399 | 400 | #[cfg(feature = "full")] 401 | #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] 402 | impl HasAttributes for syn::Pat { 403 | fn attrs(&self) -> &[syn::Attribute] { 404 | match self { 405 | syn::Pat::Const(expr) => &expr.attrs, 406 | syn::Pat::Ident(expr) => &expr.attrs, 407 | syn::Pat::Lit(expr) => &expr.attrs, 408 | syn::Pat::Macro(expr) => &expr.attrs, 409 | syn::Pat::Or(expr) => &expr.attrs, 410 | syn::Pat::Paren(expr) => &expr.attrs, 411 | syn::Pat::Path(expr) => &expr.attrs, 412 | syn::Pat::Range(expr) => &expr.attrs, 413 | syn::Pat::Reference(expr) => &expr.attrs, 414 | syn::Pat::Rest(expr) => &expr.attrs, 415 | syn::Pat::Slice(expr) => &expr.attrs, 416 | syn::Pat::Struct(expr) => &expr.attrs, 417 | syn::Pat::Tuple(expr) => &expr.attrs, 418 | syn::Pat::TupleStruct(expr) => &expr.attrs, 419 | syn::Pat::Type(expr) => &expr.attrs, 420 | syn::Pat::Wild(expr) => &expr.attrs, 421 | _ => &[], 422 | } 423 | } 424 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 425 | match self { 426 | syn::Pat::Const(expr) => Ok(&mut expr.attrs), 427 | syn::Pat::Ident(expr) => Ok(&mut expr.attrs), 428 | syn::Pat::Lit(expr) => Ok(&mut expr.attrs), 429 | syn::Pat::Macro(expr) => Ok(&mut expr.attrs), 430 | syn::Pat::Or(expr) => Ok(&mut expr.attrs), 431 | syn::Pat::Paren(expr) => Ok(&mut expr.attrs), 432 | syn::Pat::Path(expr) => Ok(&mut expr.attrs), 433 | syn::Pat::Range(expr) => Ok(&mut expr.attrs), 434 | syn::Pat::Reference(expr) => Ok(&mut expr.attrs), 435 | syn::Pat::Rest(expr) => Ok(&mut expr.attrs), 436 | syn::Pat::Slice(expr) => Ok(&mut expr.attrs), 437 | syn::Pat::Struct(expr) => Ok(&mut expr.attrs), 438 | syn::Pat::Tuple(expr) => Ok(&mut expr.attrs), 439 | syn::Pat::TupleStruct(expr) => Ok(&mut expr.attrs), 440 | syn::Pat::Type(expr) => Ok(&mut expr.attrs), 441 | syn::Pat::Wild(expr) => Ok(&mut expr.attrs), 442 | syn::Pat::Verbatim(_) => Err(syn::Error::new_spanned( 443 | self, 444 | "`HasAttributes::attrs_mut` not supported with `syn::Pat::Verbatim`", 445 | )), 446 | _ => Err(syn::Error::new_spanned( 447 | self, 448 | "`HasAttributes::attrs_mut` encountered unknown `syn::Pat` variant", 449 | )), 450 | } 451 | } 452 | } 453 | 454 | impl_has_attributes!(syn::PatIdent, #full); 455 | impl_has_attributes!(syn::PatOr, #full); 456 | impl_has_attributes!(syn::PatParen, #full); 457 | impl_has_attributes!(syn::PatReference, #full); 458 | impl_has_attributes!(syn::PatRest, #full); 459 | impl_has_attributes!(syn::PatSlice, #full); 460 | impl_has_attributes!(syn::PatStruct, #full); 461 | impl_has_attributes!(syn::PatTuple, #full); 462 | impl_has_attributes!(syn::PatTupleStruct, #full); 463 | impl_has_attributes!(syn::PatType, #full); 464 | impl_has_attributes!(syn::PatWild, #full); 465 | impl_has_attributes!(syn::Receiver, #full); 466 | 467 | #[cfg(feature = "full")] 468 | #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] 469 | impl HasAttributes for syn::TraitItem { 470 | fn attrs(&self) -> &[syn::Attribute] { 471 | match self { 472 | syn::TraitItem::Const(item) => &item.attrs, 473 | syn::TraitItem::Fn(item) => &item.attrs, 474 | syn::TraitItem::Type(item) => &item.attrs, 475 | syn::TraitItem::Macro(item) => &item.attrs, 476 | _ => &[], 477 | } 478 | } 479 | fn attrs_mut(&mut self) -> Result<&mut Vec> { 480 | match self { 481 | syn::TraitItem::Const(item) => Ok(&mut item.attrs), 482 | syn::TraitItem::Fn(item) => Ok(&mut item.attrs), 483 | syn::TraitItem::Type(item) => Ok(&mut item.attrs), 484 | syn::TraitItem::Macro(item) => Ok(&mut item.attrs), 485 | syn::TraitItem::Verbatim(_) => Err(syn::Error::new_spanned( 486 | self, 487 | "`HasAttributes::attrs_mut` not supported with `syn::TraitItem::Verbatim`", 488 | )), 489 | _ => Err(syn::Error::new_spanned( 490 | self, 491 | "`HasAttributes::attrs_mut` encountered unknown `syn::TraitItem` variant", 492 | )), 493 | } 494 | } 495 | } 496 | 497 | impl_has_attributes!(syn::TraitItemConst, #full); 498 | impl_has_attributes!(syn::TraitItemFn, #full); 499 | impl_has_attributes!(syn::TraitItemMacro, #full); 500 | impl_has_attributes!(syn::TraitItemType, #full); 501 | impl_has_attributes!(syn::TypeParam); 502 | impl_has_attributes!(syn::Variadic, #full); 503 | impl_has_attributes!(syn::Variant); 504 | 505 | /// Converts a [`HasAttributes`] type to a stored container value. 506 | /// 507 | /// This trait is only called by the `#[deluxe(container)`] attribute on fields to properly handle 508 | /// conversions to corresponding owned and [`Option`] types. 509 | /// 510 | /// Custom implementations can be provided if a wrapper for the `syn` type is needed, or if needing 511 | /// to transform the `syn` type into something else. When using a custom container type, make sure 512 | /// to set `#[deluxe(container(ty = ...))]` on the container field. The 513 | /// [`container_from`](Self::container_from) method will be called when deriving 514 | /// [`ParseAttributes`], and [`container_from_mut`](Self::container_from_mut) will be called when 515 | /// deriving [`ExtractAttributes`]. 516 | /// 517 | /// ```ignore 518 | /// struct IdentWrapper<'t>(&'t syn::Ident); 519 | /// 520 | /// impl<'t> deluxe::ContainerFrom<'t, syn::ItemFn> for IdentWrapper<'t> { 521 | /// #[inline] 522 | /// fn container_from(t: &'t syn::ItemFn) -> Self { 523 | /// Self(&t.sig.ident) 524 | /// } 525 | /// } 526 | /// 527 | /// impl<'t> deluxe::ContainerFrom<'t, syn::ItemStruct> for IdentWrapper<'t> { 528 | /// #[inline] 529 | /// fn container_from(t: &'t syn::ItemFn) -> Self { 530 | /// Self(&t.ident) 531 | /// } 532 | /// } 533 | /// 534 | /// // can only be parsed from an `ItemFn` 535 | /// #[derive(deluxe::ParseAttributes)] 536 | /// struct Func<'t> { 537 | /// id: u64, 538 | /// #[deluxe(container(ty = syn::ItemFn))] 539 | /// container: IdentWrapper<'t>, 540 | /// } 541 | /// 542 | /// // can only be parsed from an `ItemStruct` 543 | /// #[derive(deluxe::ParseAttributes)] 544 | /// struct Struct<'t> { 545 | /// id: u64, 546 | /// #[deluxe(container(ty = syn::ItemStruct))] 547 | /// container: IdentWrapper<'t>, 548 | /// } 549 | /// ``` 550 | pub trait ContainerFrom<'t, T> { 551 | /// Converts a reference to a stored container `T` into this type. 552 | #[inline] 553 | #[allow(unused)] 554 | fn container_from(t: &'t T) -> Self 555 | where 556 | Self: Sized, 557 | { 558 | unimplemented!("immutable container not supported for this type") 559 | } 560 | /// Converts a mutable reference to a stored container `T` into this type. 561 | #[inline] 562 | #[allow(unused)] 563 | fn container_from_mut(t: &'t mut T) -> Self 564 | where 565 | Self: Sized, 566 | { 567 | unimplemented!("mutable container not supported for this type") 568 | } 569 | } 570 | 571 | impl<'t, T: HasAttributes + Clone> ContainerFrom<'t, T> for T { 572 | #[inline] 573 | fn container_from(t: &'t T) -> Self { 574 | t.clone() 575 | } 576 | #[inline] 577 | fn container_from_mut(t: &'t mut T) -> Self { 578 | t.clone() 579 | } 580 | } 581 | 582 | impl<'t, T: HasAttributes + Clone> ContainerFrom<'t, T> for Option { 583 | #[inline] 584 | fn container_from(t: &'t T) -> Self { 585 | Some(t.clone()) 586 | } 587 | #[inline] 588 | fn container_from_mut(t: &'t mut T) -> Self { 589 | Some(t.clone()) 590 | } 591 | } 592 | 593 | impl<'t, T: HasAttributes> ContainerFrom<'t, T> for &'t T { 594 | #[inline] 595 | fn container_from(t: &'t T) -> Self { 596 | t 597 | } 598 | #[inline] 599 | fn container_from_mut(t: &'t mut T) -> Self { 600 | t 601 | } 602 | } 603 | 604 | impl<'t, T: HasAttributes> ContainerFrom<'t, T> for Option<&'t T> { 605 | #[inline] 606 | fn container_from(t: &'t T) -> Self { 607 | Some(t) 608 | } 609 | #[inline] 610 | fn container_from_mut(t: &'t mut T) -> Self { 611 | Some(t) 612 | } 613 | } 614 | 615 | impl<'t, T: HasAttributes> ContainerFrom<'t, T> for &'t mut T { 616 | #[inline] 617 | fn container_from_mut(t: &'t mut T) -> Self { 618 | t 619 | } 620 | } 621 | 622 | impl<'t, T: HasAttributes> ContainerFrom<'t, T> for Option<&'t mut T> { 623 | #[inline] 624 | fn container_from_mut(t: &'t mut T) -> Self { 625 | Some(t) 626 | } 627 | } 628 | -------------------------------------------------------------------------------- /core/small_string.rs: -------------------------------------------------------------------------------- 1 | /// A string type that can be stored on the stack or on the heap, and can also represent borrowed 2 | /// strings. 3 | pub struct SmallString<'a>(SmallStringInner<'a>); 4 | 5 | const MAX_INLINE_LEN: usize = 56; 6 | 7 | enum SmallStringInner<'a> { 8 | Heap(String), 9 | Inline(arrayvec::ArrayString), 10 | Borrowed(&'a str), 11 | } 12 | 13 | impl<'a> SmallString<'a> { 14 | /// Creates a new empty `SmallString`. 15 | /// 16 | /// Does not allocate. 17 | #[inline] 18 | #[must_use] 19 | pub fn new() -> Self { 20 | Self(SmallStringInner::Borrowed("")) 21 | } 22 | /// Converts this `SmallString` to `SmallString<'static>`. 23 | #[inline] 24 | #[must_use] 25 | pub fn into_owned(self) -> SmallString<'static> { 26 | use SmallStringInner::*; 27 | SmallString(match self.0 { 28 | Heap(s) => Heap(s), 29 | Inline(s) => Inline(s), 30 | Borrowed(s) => s.try_into().map(Inline).unwrap_or_else(|_| Heap(s.into())), 31 | }) 32 | } 33 | /// Extracts a string slice containing the entire `SmallString`. 34 | #[inline] 35 | #[must_use] 36 | pub fn as_str(&self) -> &str { 37 | use SmallStringInner::*; 38 | match &self.0 { 39 | Heap(s) => s.as_str(), 40 | Inline(s) => s.as_str(), 41 | Borrowed(s) => s, 42 | } 43 | } 44 | /// Appends the given [`char`] to the end of this `SmallString`. 45 | #[inline] 46 | pub fn push(&mut self, c: char) { 47 | self.push_str(c.encode_utf8(&mut [0; 4])); 48 | } 49 | /// Appends a given string slice onto the end of this `SmallString`. 50 | pub fn push_str(&mut self, s: &str) { 51 | use SmallStringInner::*; 52 | match &mut self.0 { 53 | Heap(h) => h.push_str(s), 54 | Inline(i) => { 55 | if i.try_push_str(s).is_err() { 56 | let mut v = String::from(i.as_str()); 57 | v.push_str(s); 58 | *self = Self(Heap(v)); 59 | } 60 | } 61 | Borrowed(b) => { 62 | if b.len().checked_add(s.len()).expect("integer overflow") > MAX_INLINE_LEN { 63 | let mut v = String::from(*b); 64 | v.push_str(s); 65 | *self = Self(Heap(v)); 66 | } else { 67 | let mut v = arrayvec::ArrayString::from(b).unwrap(); 68 | v.push_str(s); 69 | *self = Self(Inline(v)); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | impl<'a> Eq for SmallString<'a> {} 77 | 78 | impl<'a> PartialEq for SmallString<'a> { 79 | #[inline] 80 | fn eq(&self, other: &Self) -> bool { 81 | self.as_str().eq(other.as_str()) 82 | } 83 | } 84 | 85 | impl<'a> PartialEq for SmallString<'a> { 86 | #[inline] 87 | fn eq(&self, other: &str) -> bool { 88 | self.as_str().eq(other) 89 | } 90 | } 91 | 92 | impl<'a> PartialEq> for str { 93 | #[inline] 94 | fn eq(&self, other: &SmallString<'a>) -> bool { 95 | self.eq(other.as_str()) 96 | } 97 | } 98 | 99 | impl<'a> Ord for SmallString<'a> { 100 | #[inline] 101 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 102 | self.as_str().cmp(other.as_str()) 103 | } 104 | } 105 | 106 | impl<'a> PartialOrd for SmallString<'a> { 107 | #[inline] 108 | fn partial_cmp(&self, other: &Self) -> Option { 109 | self.as_str().partial_cmp(other.as_str()) 110 | } 111 | } 112 | 113 | impl<'a> PartialOrd for SmallString<'a> { 114 | #[inline] 115 | fn partial_cmp(&self, other: &str) -> Option { 116 | self.as_str().partial_cmp(other) 117 | } 118 | } 119 | 120 | impl<'a> PartialOrd> for str { 121 | #[inline] 122 | fn partial_cmp(&self, other: &SmallString<'a>) -> Option { 123 | self.partial_cmp(other.as_str()) 124 | } 125 | } 126 | 127 | impl<'a> std::hash::Hash for SmallString<'a> { 128 | #[inline] 129 | fn hash(&self, state: &mut H) { 130 | self.as_str().hash(state) 131 | } 132 | } 133 | 134 | impl<'a> Default for SmallString<'a> { 135 | fn default() -> Self { 136 | Self::new() 137 | } 138 | } 139 | 140 | impl<'a> std::fmt::Debug for SmallString<'a> { 141 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 142 | use SmallStringInner::*; 143 | match &self.0 { 144 | Heap(s) => s.fmt(f), 145 | Inline(s) => s.fmt(f), 146 | Borrowed(s) => s.fmt(f), 147 | } 148 | } 149 | } 150 | 151 | impl<'a> std::fmt::Display for SmallString<'a> { 152 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 153 | use SmallStringInner::*; 154 | match &self.0 { 155 | Heap(s) => s.fmt(f), 156 | Inline(s) => s.fmt(f), 157 | Borrowed(s) => s.fmt(f), 158 | } 159 | } 160 | } 161 | 162 | impl<'a> From<&'a str> for SmallString<'a> { 163 | #[inline] 164 | fn from(value: &'a str) -> Self { 165 | Self(SmallStringInner::Borrowed(value)) 166 | } 167 | } 168 | 169 | impl<'a> From for SmallString<'a> { 170 | #[inline] 171 | fn from(value: String) -> Self { 172 | if value.len() > MAX_INLINE_LEN { 173 | Self(SmallStringInner::Heap(value)) 174 | } else { 175 | Self(SmallStringInner::Inline( 176 | arrayvec::ArrayString::from(value.as_str()).unwrap(), 177 | )) 178 | } 179 | } 180 | } 181 | 182 | impl<'a> From> for SmallString<'a> { 183 | #[inline] 184 | fn from(value: std::borrow::Cow<'a, str>) -> Self { 185 | match value { 186 | std::borrow::Cow::Borrowed(b) => b.into(), 187 | std::borrow::Cow::Owned(o) => o.into(), 188 | } 189 | } 190 | } 191 | 192 | impl<'a> std::ops::Deref for SmallString<'a> { 193 | type Target = str; 194 | 195 | #[inline] 196 | fn deref(&self) -> &Self::Target { 197 | self.as_str() 198 | } 199 | } 200 | 201 | impl<'a> AsRef for SmallString<'a> { 202 | #[inline] 203 | fn as_ref(&self) -> &str { 204 | self.as_str() 205 | } 206 | } 207 | 208 | impl<'a> AsRef<[u8]> for SmallString<'a> { 209 | #[inline] 210 | fn as_ref(&self) -> &[u8] { 211 | self.as_str().as_bytes() 212 | } 213 | } 214 | 215 | impl<'a> std::borrow::Borrow for SmallString<'a> { 216 | #[inline] 217 | fn borrow(&self) -> &str { 218 | self.as_str() 219 | } 220 | } 221 | 222 | impl<'a> std::fmt::Write for SmallString<'a> { 223 | #[inline] 224 | fn write_str(&mut self, s: &str) -> std::fmt::Result { 225 | self.push_str(s); 226 | Ok(()) 227 | } 228 | #[inline] 229 | fn write_char(&mut self, c: char) -> std::fmt::Result { 230 | self.push(c); 231 | Ok(()) 232 | } 233 | } 234 | 235 | impl<'a> quote::ToTokens for SmallString<'a> { 236 | #[inline] 237 | fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 238 | self.as_str().to_tokens(tokens); 239 | } 240 | } 241 | 242 | impl<'a> crate::ParseMetaItem for SmallString<'a> { 243 | #[inline] 244 | fn parse_meta_item( 245 | input: syn::parse::ParseStream, 246 | _mode: crate::ParseMode, 247 | ) -> crate::Result { 248 | Ok(crate::parse_helpers::key_to_string( 249 | &input.parse::()?.token(), 250 | )) 251 | } 252 | } 253 | 254 | impl<'a> crate::ToKeyString for SmallString<'a> { 255 | #[inline] 256 | fn fmt_key_string(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 257 | std::fmt::Display::fmt(self, f) 258 | } 259 | #[inline] 260 | fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { 261 | f(self) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /core/util.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use quote::TokenStreamExt; 3 | use std::{ 4 | borrow::Borrow, 5 | cell::RefCell, 6 | fmt::Display, 7 | hash::Hash, 8 | ops::{Deref, DerefMut}, 9 | }; 10 | use syn::parse::{ParseBuffer, ParseStream}; 11 | 12 | use crate::{parse_helpers::inputs_span, parse_meta::*}; 13 | 14 | /// The error type for parsers. 15 | pub type Error = syn::Error; 16 | /// The result of a parse method. 17 | pub type Result = syn::Result; 18 | 19 | /// A wrapper for a list of errors. Can be empty. 20 | #[derive(Clone, Debug, Default)] 21 | #[repr(transparent)] 22 | pub struct Errors { 23 | // RefCell here so this can be re-entrant when used from parser combinators 24 | errors: RefCell>, 25 | } 26 | 27 | impl Errors { 28 | #[inline] 29 | /// Creates a new empty error list. 30 | pub const fn new() -> Self { 31 | Self { 32 | errors: RefCell::new(None), 33 | } 34 | } 35 | /// Checks if the list contains any errors. 36 | #[inline] 37 | pub fn is_empty(&self) -> bool { 38 | self.errors.borrow().is_none() 39 | } 40 | /// Reset the list to an empty state. 41 | #[inline] 42 | pub fn clear(&self) { 43 | self.errors.take(); 44 | } 45 | /// Pushes one error onto the list. This function is a wrapper around [`syn::Error::new`]. 46 | #[inline] 47 | pub fn push(&self, span: Span, message: T) { 48 | self.push_syn(Error::new(span, message)); 49 | } 50 | /// Pushes one error onto the list, setting the error's span to 51 | /// [`Span::call_site()`](proc_macro2::Span::call_site). 52 | #[inline] 53 | pub fn push_call_site(&self, message: T) { 54 | self.push(Span::call_site(), message); 55 | } 56 | /// Pushes one error onto the list spanning the given syntax tree node. This 57 | /// function is a wrapper around [`syn::Error::new_spanned`]. 58 | #[inline] 59 | pub fn push_spanned(&self, tokens: T, message: U) 60 | where 61 | T: quote::ToTokens, 62 | U: Display, 63 | { 64 | self.push_syn(Error::new_spanned(tokens, message)); 65 | } 66 | /// Pushes one previously constructed [`Error`] onto the list. 67 | #[inline] 68 | pub fn push_syn(&self, error: Error) { 69 | let mut storage = self.errors.borrow_mut(); 70 | if let Some(storage) = storage.as_mut() { 71 | storage.combine(error); 72 | } else { 73 | storage.replace(error); 74 | } 75 | } 76 | /// Pushes an error onto the list from a [`Result`]. 77 | /// 78 | /// If `result` is [`Err`], pushes the error and returns [`None`]. If `result` is [`Ok`], 79 | /// returns [Some]\(T). 80 | #[inline] 81 | pub fn push_result(&self, result: Result) -> Option { 82 | match result { 83 | Ok(t) => Some(t), 84 | Err(e) => { 85 | self.push_syn(e); 86 | None 87 | } 88 | } 89 | } 90 | /// Appends all errors from `iter` into this list. 91 | #[inline] 92 | pub fn extend>(&self, iter: T) { 93 | let mut errors = self.errors.borrow_mut(); 94 | if let Some(errors) = errors.as_mut() { 95 | errors.extend(iter); 96 | } else { 97 | let mut iter = iter.into_iter(); 98 | if let Some(next) = iter.next() { 99 | let errors = errors.insert(next); 100 | errors.extend(iter); 101 | } 102 | } 103 | } 104 | /// Returns `Err` if the list has errors, or `Ok(value)` if the list is empty. 105 | /// 106 | /// If the list has any errors, returns [`Err`] containing one [`Error`] with all of the errors 107 | /// combined using [`Error::combine`](syn::Error::combine). 108 | #[inline] 109 | pub fn into_result(self, value: T) -> Result { 110 | if let Some(err) = self.errors.take() { 111 | Err(err) 112 | } else { 113 | Ok(value) 114 | } 115 | } 116 | /// Checks if the error list is empty. 117 | /// 118 | /// If the list has any errors, returns [`Err`] containing one [`Error`] with all of the errors 119 | /// combined using [`Error::combine`](syn::Error::combine). Otherwise, returns [`Ok`]. 120 | #[inline] 121 | pub fn check(self) -> Result<()> { 122 | self.into_result(()) 123 | } 124 | /// Returns the inner if the error list has errors. 125 | /// 126 | /// # Panics 127 | /// 128 | /// Panics if the error list is empty. 129 | #[inline] 130 | pub fn unwrap_err(self) -> Error { 131 | if let Some(err) = self.errors.take() { 132 | err 133 | } else { 134 | panic!("expected Errors to not be empty"); 135 | } 136 | } 137 | /// Returns a new `Err` if the error list has errors. 138 | /// 139 | /// # Panics 140 | /// 141 | /// Panics if the error list is empty. 142 | #[inline] 143 | pub fn bail(self) -> Result { 144 | Err(self.unwrap_err()) 145 | } 146 | /// Converts the error list into a token stream containing [`std::compile_error`] invocations. 147 | /// 148 | /// The errors are generated with [`Error::into_compile_error`](syn::Error::into_compile_error). 149 | /// 150 | /// Returns [`None`] if the list is empty. 151 | #[inline] 152 | pub fn into_compile_error(self) -> Option { 153 | self.errors.take().map(|e| e.into_compile_error()) 154 | } 155 | /// Returns an iterator of token streams containing [`std::compile_error`] invocations. 156 | /// 157 | /// Each token stream will contain one invocation. The errors are generated with 158 | /// [`Error::into_compile_error`](syn::Error::into_compile_error). 159 | #[inline] 160 | pub fn into_compile_errors(self) -> impl IntoIterator { 161 | self.errors 162 | .take() 163 | .into_iter() 164 | .map(|e| e.into_compile_error()) 165 | } 166 | /// Creates a token stream containing the current set of errors and `item`. 167 | pub fn output_with(self, item: Q) -> TokenStream { 168 | let mut tokens = item.into_token_stream(); 169 | quote::ToTokens::to_tokens(&self, &mut tokens); 170 | tokens 171 | } 172 | } 173 | 174 | impl quote::ToTokens for Errors { 175 | #[inline] 176 | fn to_tokens(&self, tokens: &mut TokenStream) { 177 | tokens.extend(self.to_token_stream()); 178 | } 179 | fn to_token_stream(&self) -> TokenStream { 180 | self.errors 181 | .borrow() 182 | .as_ref() 183 | .map(|e| e.to_compile_error()) 184 | .unwrap_or_default() 185 | } 186 | #[inline] 187 | fn into_token_stream(self) -> TokenStream 188 | where 189 | Self: Sized, 190 | { 191 | self.into_compile_error().unwrap_or_default() 192 | } 193 | } 194 | 195 | impl From for Errors { 196 | #[inline] 197 | fn from(err: Error) -> Self { 198 | Self { 199 | errors: RefCell::new(Some(err)), 200 | } 201 | } 202 | } 203 | 204 | impl FromIterator for Errors { 205 | #[inline] 206 | fn from_iter>(iter: T) -> Self { 207 | let mut iter = iter.into_iter(); 208 | let errors = iter.next().map(|mut first| { 209 | first.extend(iter); 210 | first 211 | }); 212 | Self { 213 | errors: RefCell::new(errors), 214 | } 215 | } 216 | } 217 | 218 | impl IntoIterator for Errors { 219 | type Item = Error; 220 | type IntoIter = ErrorsIntoIter; 221 | #[inline] 222 | fn into_iter(self) -> Self::IntoIter { 223 | ErrorsIntoIter { 224 | errors: self.errors.take().map(|e| e.into_iter()), 225 | } 226 | } 227 | } 228 | 229 | /// An iterator containing all the errors in an [`Errors`]. 230 | pub struct ErrorsIntoIter { 231 | errors: Option<::IntoIter>, 232 | } 233 | 234 | impl Iterator for ErrorsIntoIter { 235 | type Item = Error; 236 | fn next(&mut self) -> Option { 237 | self.errors.as_mut().and_then(|e| e.next()) 238 | } 239 | } 240 | 241 | /// A wrapper for adding a [`Span`](proc_macro2::Span) to an arbitrary value. 242 | /// 243 | /// Implementations are provided for all the parsing traits that simply delegate to `T`, capturing 244 | /// the inital [`Span`](proc_macro2::Span) from the [`ParseStream`](syn::parse::ParseStream). 245 | #[derive(Copy, Clone, Debug)] 246 | pub struct SpannedValue { 247 | value: T, 248 | span: Span, 249 | } 250 | 251 | impl SpannedValue { 252 | /// Creates a new value wrapping a T, with the span set to 253 | /// [`Span::call_site`](proc_macro2::Span::call_site). 254 | #[inline] 255 | pub fn new(value: T) -> Self { 256 | Self::with_span(value, Span::call_site()) 257 | } 258 | /// Creates a new value wrapping a T, with the span set to `span`. 259 | #[inline] 260 | pub fn with_span(value: T, span: Span) -> Self { 261 | Self { value, span } 262 | } 263 | /// Unwraps a `SpannedValue` into a `T`. Note this is an associated function, not a method. 264 | #[inline] 265 | pub fn into_inner(value: SpannedValue) -> T { 266 | value.value 267 | } 268 | } 269 | 270 | impl Default for SpannedValue { 271 | #[inline] 272 | fn default() -> Self { 273 | Self::new(T::default()) 274 | } 275 | } 276 | 277 | impl Display for SpannedValue { 278 | #[inline] 279 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 280 | self.value.fmt(f) 281 | } 282 | } 283 | 284 | impl PartialEq for SpannedValue { 285 | #[inline] 286 | fn eq(&self, other: &Self) -> bool { 287 | self.value == other.value 288 | } 289 | } 290 | 291 | impl Eq for SpannedValue {} 292 | 293 | impl PartialOrd for SpannedValue { 294 | #[inline] 295 | fn partial_cmp(&self, other: &Self) -> Option { 296 | self.value.partial_cmp(&other.value) 297 | } 298 | } 299 | 300 | impl Ord for SpannedValue { 301 | #[inline] 302 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 303 | self.value.cmp(&other.value) 304 | } 305 | } 306 | 307 | impl Hash for SpannedValue { 308 | #[inline] 309 | fn hash(&self, state: &mut H) { 310 | self.value.hash(state); 311 | } 312 | } 313 | 314 | impl quote::ToTokens for SpannedValue { 315 | #[inline] 316 | fn to_tokens(&self, tokens: &mut TokenStream) { 317 | let mut group = proc_macro2::Group::new(proc_macro2::Delimiter::None, Default::default()); 318 | group.set_span(self.span); 319 | tokens.append(group); 320 | } 321 | } 322 | 323 | impl ParseMetaItem for SpannedValue { 324 | #[inline] 325 | fn parse_meta_item(input: ParseStream, mode: crate::ParseMode) -> Result { 326 | let span = input.span(); 327 | let value = T::parse_meta_item(input, mode)?; 328 | let span = input.span().join(span).unwrap_or(span); 329 | Ok(Self { value, span }) 330 | } 331 | #[inline] 332 | fn parse_meta_item_inline<'s, S: Borrow>>( 333 | inputs: &[S], 334 | mode: ParseMode, 335 | ) -> Result { 336 | let span = inputs.first().map(|p| p.borrow().span()); 337 | let value = T::parse_meta_item_inline(inputs, mode)?; 338 | let span = span 339 | .and_then(|s| inputs.last().and_then(|p| p.borrow().span().join(s))) 340 | .unwrap_or_else(Span::call_site); 341 | Ok(Self { value, span }) 342 | } 343 | #[inline] 344 | fn parse_meta_item_flag(span: Span) -> Result { 345 | Ok(Self { 346 | value: T::parse_meta_item_flag(span)?, 347 | span, 348 | }) 349 | } 350 | #[inline] 351 | fn parse_meta_item_named(input: ParseStream, name: &str, span: Span) -> Result { 352 | let value = T::parse_meta_item_named(input, name, span)?; 353 | let span = input.span().join(span).unwrap_or(span); 354 | Ok(Self { value, span }) 355 | } 356 | } 357 | 358 | impl ParseMetaFlatUnnamed for SpannedValue { 359 | #[inline] 360 | fn field_count() -> Option { 361 | T::field_count() 362 | } 363 | fn parse_meta_flat_unnamed<'s, S: Borrow>>( 364 | inputs: &[S], 365 | mode: ParseMode, 366 | index: usize, 367 | ) -> Result { 368 | let mut span = crate::parse_helpers::inputs_span(inputs); 369 | let value = T::parse_meta_flat_unnamed(inputs, mode, index)?; 370 | if let Some(closed) = span.join(inputs_span(inputs)) { 371 | span = closed; 372 | } 373 | Ok(Self { value, span }) 374 | } 375 | } 376 | 377 | impl ParseMetaFlatNamed for SpannedValue { 378 | const ACCEPTS_ALL: bool = T::ACCEPTS_ALL; 379 | #[inline] 380 | fn field_names() -> &'static [&'static str] { 381 | T::field_names() 382 | } 383 | fn parse_meta_flat_named<'s, S: Borrow>>( 384 | inputs: &[S], 385 | mode: ParseMode, 386 | prefix: &str, 387 | validate: bool, 388 | ) -> Result { 389 | let mut span = crate::parse_helpers::inputs_span(inputs); 390 | let value = T::parse_meta_flat_named(inputs, mode, prefix, validate)?; 391 | if let Some(closed) = span.join(inputs_span(inputs)) { 392 | span = closed; 393 | } 394 | Ok(Self { value, span }) 395 | } 396 | } 397 | 398 | impl ParseMetaAppend for SpannedValue { 399 | fn parse_meta_append<'s, S, I, P>(inputs: &[S], paths: I) -> Result 400 | where 401 | S: Borrow>, 402 | I: IntoIterator, 403 | I::IntoIter: Clone, 404 | P: AsRef, 405 | { 406 | let mut span = inputs_span(inputs); 407 | let value = T::parse_meta_append(inputs, paths)?; 408 | if let Some(closed) = span.join(inputs_span(inputs)) { 409 | span = closed; 410 | } 411 | Ok(Self { value, span }) 412 | } 413 | } 414 | 415 | impl ParseMetaRest for SpannedValue { 416 | fn parse_meta_rest<'s, S: Borrow>>( 417 | inputs: &[S], 418 | exclude: &[&str], 419 | ) -> Result { 420 | let mut span = inputs_span(inputs); 421 | let value = T::parse_meta_rest(inputs, exclude)?; 422 | if let Some(closed) = span.join(inputs_span(inputs)) { 423 | span = closed; 424 | } 425 | Ok(Self { value, span }) 426 | } 427 | } 428 | 429 | impl From for SpannedValue { 430 | #[inline] 431 | fn from(value: T) -> Self { 432 | Self { 433 | value, 434 | span: Span::call_site(), 435 | } 436 | } 437 | } 438 | 439 | impl Deref for SpannedValue { 440 | type Target = T; 441 | #[inline] 442 | fn deref(&self) -> &Self::Target { 443 | &self.value 444 | } 445 | } 446 | 447 | impl DerefMut for SpannedValue { 448 | #[inline] 449 | fn deref_mut(&mut self) -> &mut Self::Target { 450 | &mut self.value 451 | } 452 | } 453 | 454 | /// A value for a boolean named field that can only be a name (set) or omitted (unset). 455 | /// 456 | /// Similar to an [Option]<[SpannedValue]<[bool]>> but does not allow `=` or 457 | /// `()` after the field name. Thus, it is only useful with named fields. Parsing this out of a 458 | /// tuple struct or tuple variant will always result in a parse error. 459 | /// 460 | /// It is not necessary to use [`#[deluxe(default)]`](ParseMetaItem#deluxedefault-1) on a field 461 | /// using this type. The field will automatically be created with a `false` value if the name is 462 | /// omitted. 463 | #[derive(Copy, Clone, Debug, Default)] 464 | pub struct Flag(Option); 465 | 466 | impl Flag { 467 | /// Creates a new `true` flag value spanned to `span`. 468 | #[inline] 469 | pub fn set(span: Span) -> Self { 470 | Self(Some(span)) 471 | } 472 | /// Creates a new `true` flag value spanned to [`Span::call_site`]. 473 | #[inline] 474 | pub fn set_call_site() -> Self { 475 | Self(Some(Span::call_site())) 476 | } 477 | /// Creates a new `false` flag value. 478 | #[inline] 479 | pub fn unset() -> Self { 480 | Self(None) 481 | } 482 | /// Returns `true` if the flag was set. 483 | #[inline] 484 | pub fn is_set(&self) -> bool { 485 | self.0.is_some() 486 | } 487 | } 488 | 489 | impl From for Flag { 490 | #[inline] 491 | fn from(value: bool) -> Self { 492 | Self(value.then(Span::call_site)) 493 | } 494 | } 495 | 496 | impl From for bool { 497 | #[inline] 498 | fn from(value: Flag) -> Self { 499 | value.is_set() 500 | } 501 | } 502 | 503 | impl Eq for Flag {} 504 | 505 | impl PartialEq for Flag { 506 | #[inline] 507 | fn eq(&self, other: &Self) -> bool { 508 | self.is_set() == other.is_set() 509 | } 510 | } 511 | 512 | impl PartialEq for Flag { 513 | #[inline] 514 | fn eq(&self, other: &bool) -> bool { 515 | self.is_set() == *other 516 | } 517 | } 518 | 519 | impl PartialEq for bool { 520 | #[inline] 521 | fn eq(&self, other: &Flag) -> bool { 522 | *self == other.is_set() 523 | } 524 | } 525 | 526 | impl quote::ToTokens for Flag { 527 | #[inline] 528 | fn to_tokens(&self, tokens: &mut TokenStream) { 529 | tokens.append(proc_macro2::Ident::new( 530 | self.0.map(|_| "true").unwrap_or("false"), 531 | self.0.unwrap_or_else(Span::call_site), 532 | )); 533 | } 534 | } 535 | 536 | impl ParseMetaItem for Flag { 537 | #[inline] 538 | fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { 539 | Self::parse_meta_item_inline(&[input], mode) 540 | } 541 | #[inline] 542 | fn parse_meta_item_inline<'s, S: Borrow>>( 543 | inputs: &[S], 544 | _mode: ParseMode, 545 | ) -> Result { 546 | Err(Error::new( 547 | crate::parse_helpers::inputs_span(inputs), 548 | "field with type `Flag` can only be a named field with no value", 549 | )) 550 | } 551 | #[inline] 552 | fn parse_meta_item_flag(span: Span) -> Result { 553 | Ok(Self(Some(span))) 554 | } 555 | #[inline] 556 | fn parse_meta_item_named(input: ParseStream, _name: &str, span: Span) -> Result { 557 | if input.is_empty() || input.peek(syn::Token![,]) { 558 | Self::parse_meta_item_flag(span) 559 | } else { 560 | Err(Error::new(input.span(), "unexpected token")) 561 | } 562 | } 563 | #[inline] 564 | fn missing_meta_item(_name: &str, _span: Span) -> Result { 565 | Ok(Self::unset()) 566 | } 567 | } 568 | -------------------------------------------------------------------------------- /core/validations.rs: -------------------------------------------------------------------------------- 1 | //! Additional helper functions for validating after parsing. 2 | 3 | use crate::Errors; 4 | 5 | /// Appends an error if more than one given attribute is present. 6 | /// 7 | /// `attrs` should provide an iterator of tuples containing the field name, and an [`Option`] 8 | /// possibly containing the field value. If two or more are [`Some`], an error will be appended to 9 | /// `errors`, with `prefix` prepended onto the names. 10 | pub fn only_one<'t, I, O>(attrs: I, prefix: &str, errors: &Errors) 11 | where 12 | I: IntoIterator, 13 | I::IntoIter: Clone, 14 | O: Into> + Clone + ?Sized + 't, 15 | { 16 | let iter = attrs.into_iter(); 17 | let present_spans = iter.clone().filter_map(|f| f.1.clone().into()); 18 | if present_spans.clone().take(2).count() == 2 { 19 | let mut names = String::new(); 20 | for (n, _) in iter { 21 | use std::fmt::Write; 22 | let n = crate::parse_helpers::join_path(prefix, n); 23 | if names.is_empty() { 24 | write!(names, "`{n}`").unwrap(); 25 | } else { 26 | write!(names, ", `{n}`").unwrap(); 27 | } 28 | } 29 | for span in present_spans { 30 | errors.push(span.span(), format!("only one of {names} is allowed")); 31 | } 32 | } 33 | } 34 | 35 | /// Appends an error if some attributes are present, but not all of them. 36 | /// 37 | /// `attrs` should provide an iterator of tuples containing the field name, and an [`Option`] 38 | /// possibly containing the field value. If at least one is [`Some`] and at least one is [`None`], 39 | /// an error will be appended to `errors`, with `prefix` prepended onto the names. 40 | pub fn all_or_none<'t, I, O>(attrs: I, prefix: &str, errors: &Errors) 41 | where 42 | I: IntoIterator, 43 | I::IntoIter: Clone, 44 | O: Into> + Clone + ?Sized + 't, 45 | { 46 | let iter = attrs.into_iter(); 47 | let mut search = iter.clone().cloned().map(|f| f.1.into()); 48 | let first_is_some = search.next().map(|f| f.is_some()).unwrap_or(false); 49 | if search.any(|f| f.is_some() != first_is_some) { 50 | let mut names = String::new(); 51 | for (n, o) in iter.clone().cloned() { 52 | if o.into().is_none() { 53 | use std::fmt::Write; 54 | let n = crate::parse_helpers::join_path(prefix, n); 55 | if names.is_empty() { 56 | write!(names, "`{n}`").unwrap(); 57 | } else { 58 | write!(names, ", `{n}`").unwrap(); 59 | } 60 | } 61 | } 62 | for (n, o) in iter.cloned() { 63 | if let Some(o) = o.into() { 64 | errors.push( 65 | o.span(), 66 | format!("{names} must also be set in order to use `{n}`"), 67 | ); 68 | } 69 | } 70 | } 71 | } 72 | 73 | #[macro_export] 74 | #[doc(hidden)] 75 | macro_rules! _spanned_arg { 76 | (($name:expr, $value:expr)) => { 77 | &( 78 | $name, 79 | $value.map(|s| s as &dyn $crate::syn::spanned::Spanned), 80 | ) 81 | }; 82 | ($field:ident) => { 83 | &( 84 | $crate::stringify!($field), 85 | $field 86 | .as_ref() 87 | .map(|s| s as &dyn $crate::syn::spanned::Spanned), 88 | ) 89 | }; 90 | } 91 | 92 | /// Appends an error if more than one given attribute is present, automatically casting and 93 | /// stringifying field names. 94 | /// 95 | /// Convenience macro for [`only_one`](fn@only_one). The first argument is a &[str] 96 | /// prefix to prepend onto the names in the event of an error. The second argument is an 97 | /// &[Errors] to add any errors into. 98 | /// 99 | /// The rest of the arguments are either identifiers or 2-element tuples. Identifiers must be an 100 | /// `Option` and tuples must be `(&str, Option<&T>)`, where `&T` is castable to &dyn 101 | /// [syn::spanned::Spanned]. Identifiers will automatically [`stringify`] the identifier 102 | /// name to use in the error message, and so should used when the field name is the same as the 103 | /// variable name. The tuple form can be used to specify a different field name from the name of 104 | /// the variable. The identifier form is intended to make it easier to do validations by 105 | /// destructuring a data type after it has been parsed. 106 | /// 107 | /// # Example 108 | /// 109 | /// ``` 110 | /// #[derive(Default)] 111 | /// struct MyStruct { 112 | /// my_bool: Option, 113 | /// path: Option, 114 | /// // #[deluxe(rename = "ty")] 115 | /// field3: Option, 116 | /// } 117 | /// let errors = deluxe_core::Errors::new(); 118 | /// let s = MyStruct::default(); 119 | /// 120 | /// // ... parsing omitted ... 121 | /// 122 | /// let MyStruct { my_bool, path, field3 } = &s; 123 | /// // only one of `my_bool`, `path`, `ty` is allowed 124 | /// deluxe_core::only_one!("", &errors, my_bool, path, ("ty", field3.as_ref())); 125 | /// # assert!(errors.is_empty()); 126 | /// ``` 127 | #[macro_export] 128 | macro_rules! only_one { 129 | ($prefix:expr, $errors:expr $(, $fields:tt)* $(,)?) => { 130 | $crate::validations::only_one([$($crate::_spanned_arg!($fields)),*], $prefix, $errors) 131 | }; 132 | } 133 | 134 | /// Appends an error if some attributes are present, but not all of them, automatically casting and 135 | /// stringifying field names. 136 | /// 137 | /// Convenience macro for [`all_or_none`](fn@all_or_none). The first argument is a 138 | /// &[str] prefix to prepend onto the names in the event of an error. The second 139 | /// argument is an &[Errors] to add any errors into. 140 | /// 141 | /// The rest of the arguments are either identifiers or 2-element tuples. Identifiers must be an 142 | /// `Option` and tuples must be `(&str, Option<&T>)`, where `&T` is castable to &dyn 143 | /// [syn::spanned::Spanned]. Identifiers will automatically [`stringify`] the identifier 144 | /// name to use in the error message, and so should used when the field name is the same as the 145 | /// variable name. The tuple form can be used to specify a different field name from the name of 146 | /// the variable. The identifier form is intended to make it easier to do validations by 147 | /// destructuring a data type after it has been parsed. 148 | /// 149 | /// # Example 150 | /// 151 | /// ``` 152 | /// #[derive(Default)] 153 | /// struct MyStruct { 154 | /// my_bool: Option, 155 | /// path: Option, 156 | /// // #[deluxe(rename = "ty")] 157 | /// field3: Option, 158 | /// } 159 | /// let errors = deluxe_core::Errors::new(); 160 | /// let s = MyStruct::default(); 161 | /// 162 | /// // ... parsing omitted ... 163 | /// 164 | /// let MyStruct { my_bool, path, field3 } = &s; 165 | /// // if any of `my_bool`, `path`, `ty` is present, then all must be present 166 | /// deluxe_core::all_or_none!("", &errors, my_bool, path, ("ty", field3.as_ref())); 167 | /// # assert!(errors.is_empty()); 168 | /// ``` 169 | #[macro_export] 170 | macro_rules! all_or_none { 171 | ($prefix:expr, $errors:expr $(, $fields:tt)* $(,)?) => { 172 | $crate::validations::all_or_none([$($crate::_spanned_arg!($fields)),*], $prefix, $errors) 173 | }; 174 | } 175 | -------------------------------------------------------------------------------- /macros/COPYING: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deluxe-macros" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "Derive macros for Deluxe procedural macro attribute parser" 6 | license = "MIT" 7 | documentation = "https://docs.rs/deluxe-macros" 8 | homepage = "https://github.com/jf2048/deluxe" 9 | repository = "https://github.com/jf2048/deluxe.git" 10 | 11 | [lib] 12 | path = "lib.rs" 13 | proc-macro = true 14 | 15 | [dependencies] 16 | heck = "0.4.0" 17 | if_chain = "1.0.2" 18 | proc-macro2 = "1.0.38" 19 | proc-macro-crate = "1.1.3" 20 | quote = "1.0.25" 21 | syn = { version = "2.0.10", features = ["parsing", "proc-macro"], default-features = false } 22 | deluxe-core = { path = "../core", version = "0.5.0", default-features = false } 23 | -------------------------------------------------------------------------------- /macros/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Deluxe Macros 2 | //! 3 | //! Procedural derive macros for [deluxe](https://docs.rs/deluxe). See the documentation of that 4 | //! crate for an overview. 5 | 6 | #![deny(missing_docs)] 7 | #![deny(unsafe_code)] 8 | 9 | #[macro_use] 10 | mod util; 11 | mod parse_attributes; 12 | mod parse_meta_item; 13 | mod types; 14 | use util::*; 15 | 16 | use deluxe_core::Errors; 17 | use proc_macro::TokenStream; 18 | 19 | /// Generates [`ExtractAttributes`](deluxe_core::ExtractAttributes) for a struct or enum. 20 | /// 21 | /// This macro is identical to [`ParseAttributes`], except for the following differences: 22 | /// - The generated [`extract_attributes`](deluxe_core::ExtractAttributes::extract_attributes) 23 | /// function will remove any attributes matching the paths listed in 24 | /// [`#[deluxe(attributes(...))]`](ParseAttributes#deluxeattributes). 25 | /// 26 | /// - Reference types for [`#[deluxe(container)]`](ParseAttributes#deluxecontainer) fields will not 27 | /// work. The container type must be an owned type or an [`Option`], and the container will 28 | /// always be [`clone`](Clone::clone)d into the field. The cloning will happen after the 29 | /// attributes have been extracted. Using the `lifetime` parameter for custom container types 30 | /// will also not work. 31 | /// 32 | /// See the [`ParseAttributes`] and [`ParseMetaItem`] documentation for all other details. 33 | #[proc_macro_derive(ExtractAttributes, attributes(deluxe))] 34 | pub fn derive_extract_attributes(item: TokenStream) -> TokenStream { 35 | let errors = Errors::new(); 36 | let mut tokens = util::parse::(item, &errors) 37 | .map(|input| { 38 | parse_attributes::impl_parse_attributes(input, &errors, parse_attributes::Mode::Extract) 39 | }) 40 | .unwrap_or_default(); 41 | tokens.extend(errors.into_compile_errors()); 42 | tokens.into() 43 | } 44 | 45 | /// Generates [`ParseAttributes`](deluxe_core::ParseAttributes) for a struct or enum. 46 | /// 47 | /// Supported attributes are the same as in [`ParseMetaItem`]. Additionally, some extra attributes 48 | /// are supported. 49 | /// 50 | /// ### Extra Container Attributes 51 | /// 52 | /// The following extra attributes are supported on structs and enums: 53 | /// 54 | /// - ##### `#[deluxe(attributes(...))]` 55 | /// 56 | /// Provides a list of paths to match attribute names against. These paths will be checked inside 57 | /// the [`ParseAttributes::path_matches`](deluxe_core::ParseAttributes::path_matches) function to 58 | /// see if a path matches. This attribute can be specified multiple times to append more paths to 59 | /// the list of matching paths. 60 | /// 61 | /// Omitting this attribute will cause *all* paths to match. This can be useful when doing your 62 | /// own filtering on an attribute list before parsing. 63 | /// 64 | /// ### Extra Field Attributes 65 | /// 66 | /// The following extra attributes are supported on struct fields and enum fields: 67 | /// 68 | /// - ##### `#[deluxe(container)]` 69 | /// 70 | /// Specifies that this field should store the current `obj` being parsed by 71 | /// [`parse_attributes`](deluxe_core::ParseAttributes::parse_attributes). The field can be a 72 | /// value type, an [`Option`], or a reference type. If the field is not a reference type, the 73 | /// container will be [`clone`](Clone::clone)d into the field. 74 | /// 75 | /// If the type of the container contains a lifetime, Deluxe will try to use it for a lifetime 76 | /// bound for [`ParseAttributes`](deluxe_core::ParseAttributes). If the container type is a 77 | /// reference, it will try to infer it from the lietime of the reference. Otherwise, it will try 78 | /// to take the first lifetime parameter used by the type if one is present. If no lifetimes can 79 | /// be inferred, a new lifetime parameter will be generated. See 80 | /// [`container(lifetime = ...)`](#deluxecontainerlifetime--t-ty--pathmytype) for instructions on 81 | /// how to specify a lifetime parameter manually. 82 | /// 83 | /// If the struct/enum also derives [`ParseMetaItem`], then 84 | /// [`#[deluxe(default)]`](ParseMetaItem#deluxedefault-1) is implied on any container fields. In 85 | /// that case, a common pattern is to use an [`Option`] as the container field. It will be set to 86 | /// [`None`] when calling [`parse_meta_item`](deluxe_core::ParseMetaItem::parse_meta_item), but 87 | /// will be [`Some`] when calling 88 | /// [`parse_attributes`](deluxe_core::ParseAttributes::parse_attributes). 89 | /// 90 | /// If used within an enum, only the first container field will supply the trait bounds. Any 91 | /// other container fields in other variants must have a compatible type and lifetime. 92 | /// 93 | /// Fields with this attribute can safely be added to a struct or variant using 94 | /// [`#[deluxe(transparent)]`](ParseMetaItem#deluxetransparent). Container fields do not count as 95 | /// a parseable field, as they are never parsed from tokens. 96 | /// 97 | /// - ##### `#[deluxe(container(lifetime = 't, ty = path::MyType)]` 98 | /// 99 | /// Specifies that this field should store the current `obj` being parsed by 100 | /// [`parse_attributes`](deluxe_core::ParseAttributes::parse_attributes), with a custom lifetime 101 | /// and type bound used for the [`ParseAttributes`](deluxe_core::ParseAttributes) trait 102 | /// implementation. 103 | /// 104 | /// Normally the `lifetime` and `ty` are not needed when using 105 | /// [`#[deluxe(container)]`](#deluxecontainer) because the macro can infer the lifetime and type 106 | /// from the field itself. If Deluxe is unable to infer them, these attributes can be supplied to 107 | /// manually provide the lifetime and type. `lifetime` can be omitted if the container is an 108 | /// owning type. 109 | /// 110 | /// This attribute is implemented by calling methods on the 111 | /// [`ContainerFrom`](deluxe_core::ContainerFrom) trait. Other container types besides references 112 | /// and [`Option`] will also need to provide an implementation for that trait. 113 | #[proc_macro_derive(ParseAttributes, attributes(deluxe))] 114 | pub fn derive_parse_attributes(item: TokenStream) -> TokenStream { 115 | let errors = Errors::new(); 116 | let mut tokens = util::parse::(item, &errors) 117 | .map(|input| { 118 | parse_attributes::impl_parse_attributes(input, &errors, parse_attributes::Mode::Parse) 119 | }) 120 | .unwrap_or_default(); 121 | tokens.extend(errors.into_compile_errors()); 122 | tokens.into() 123 | } 124 | 125 | /// Generates [`ParseMetaItem`](deluxe_core::ParseMetaItem) for a struct or enum. 126 | /// 127 | /// ### Container Attributes 128 | /// 129 | /// The following attributes are supported on structs and enums: 130 | /// 131 | /// - ##### `#[deluxe(default)]` 132 | /// 133 | /// Initializes the container with [`Default::default`] before parsing. 134 | /// 135 | /// - ##### `#[deluxe(default = expr)]` 136 | /// 137 | /// Initializes the container with the value of `expr` before parsing. The expression will 138 | /// be evaluated every time it is needed to construct the field, and must evaluate to a value of 139 | /// the same type as the field. 140 | /// 141 | /// - ##### `#[deluxe(transparent)]` 142 | /// 143 | /// Parses a struct with one field as if it were the field. Can only be used on a struct with a 144 | /// single parseable field. Analogous to `#[repr(transparent)]`. The struct can still contain 145 | /// fields that are [`skip`](#deluxeskip-1), as those will be ignored by `transparent`. 146 | /// 147 | /// - ##### `#[deluxe(transparent(flatten_named)]` 148 | /// 149 | /// `#[deluxe(transparent(flatten_unnamed)]` 150 | /// 151 | /// `#[deluxe(transparent(flatten_unnamed, append)]` 152 | /// 153 | /// `#[deluxe(transparent(rest)]` 154 | /// 155 | /// Parses a struct with one field as if it were the field, additionally implementing the traits 156 | /// required to use `flatten`, `rest`, or `append`. Can only be used on a struct with a 157 | /// single parseable field. 158 | /// 159 | /// Currently, it is required to provide these additional attributes to generate the trait 160 | /// definitions to use [`flatten`](#deluxeflatten-1), [`append`](#deluxeappend) or 161 | /// [`rest`](#deluxerest) on this type. 162 | /// 163 | /// - ##### `#[deluxe(and_then = expr)]` 164 | /// 165 | /// Executes an additional function ater parsing to perform additional transformations or 166 | /// validation on the input. 167 | /// 168 | /// This attribute is a simple wrapper around 169 | /// [`Result::and_then`](deluxe_core::Result::and_then). The function returned by `expr` must 170 | /// conform to the signature fn(T) -> [deluxe::Result](deluxe_core::Result)<T> 171 | /// where `T` is the type of the struct/enum being parsed. Returning 172 | /// [`Err`](deluxe_core::Result::Err) will cause the entire parse to fail. 173 | /// 174 | /// This attribute can be specified multiple times. When multiple `and_then` attributes are 175 | /// present, Deluxe will chain each function in the order the attributes were specified. 176 | /// 177 | /// ##### Example 178 | /// 179 | /// ```ignore 180 | /// #[derive(deluxe::ParseMetaItem)] 181 | /// #[deluxe(and_then = Self::validate)] 182 | /// struct Data(i32); 183 | /// impl Data { 184 | /// fn validate(self) -> deluxe::Result { 185 | /// // ... perform some checks here ... 186 | /// Ok(self) 187 | /// } 188 | /// } 189 | /// ``` 190 | /// 191 | /// - ##### `#[deluxe(allow_unknown_fields)]` 192 | /// 193 | /// Ignore any tokens and do not generate an error when an unknown field is encountered. 194 | /// 195 | /// - ##### `#[deluxe(crate = path)]` 196 | /// 197 | /// Specifies `path` as a custom path to the `deluxe` crate. Useful if `proc_macro_crate` is 198 | /// unable to find the `deluxe` crate, for instance if the crate is only re-exported inside 199 | /// another dependency. 200 | /// 201 | /// ### Variant Attributes 202 | /// 203 | /// The following attributes are supported on variants: 204 | /// 205 | /// - ##### `#[deluxe(rename = ident)]` 206 | /// 207 | /// Parse the variant with the given `ident` instead of its Rust name. 208 | /// 209 | /// - ##### `#[deluxe(alias = ident)]` 210 | /// 211 | /// Parse the variant with the given `ident`, or its Rust name. Can be repeated multiple times to 212 | /// provide additional aliases. 213 | /// 214 | /// - ##### `#[deluxe(transparent)]` 215 | /// 216 | /// Parses a variant with one field as if it were the field. Can only be used on a variant with a 217 | /// single parseable field. Analogous to `#[repr(transparent)]`. The variant can still contain 218 | /// fields that are [`skip`](#deluxeskip-1), as those will be ignored by `transparent`. 219 | /// 220 | /// - ##### `#[deluxe(flatten)]` 221 | /// 222 | /// Flattens the variant so that its unique fields are used as the key for this variant instead 223 | /// of its name. Can be used on multiple variants as long as they each have a unique set of 224 | /// parseable fields that can be used to identify the variant. Fields with 225 | /// [`flatten`](#deluxeflatten-1), [`append`](#deluxeappend) or [`rest`](#deluxerest) are not 226 | /// counted as unique fields as their field names are ignored. 227 | /// 228 | /// A single variant with no parseable fields can also be flattened. In that case, that variant 229 | /// will always be parsed as the default variant. Setting a default variant in this way is 230 | /// mutually exclusive with using [`#[deluxe(default)]`](#deluxedefault) on the enum. 231 | /// 232 | /// - ##### `#[deluxe(skip)]` 233 | /// 234 | /// Skips this variant from parsing entirely. 235 | /// 236 | /// - ##### `#[deluxe(allow_unknown_fields)]` 237 | /// 238 | /// Ignore any tokens and do not generate an error when an unknown field is encountered in this 239 | /// variant. 240 | /// 241 | /// ### Field Attributes 242 | /// 243 | /// The following attributes are supported on struct fields and enum fields: 244 | /// 245 | /// - ##### `#[deluxe(rename = ident)]` 246 | /// 247 | /// Parse the field with the given `ident` instead of its Rust name. 248 | /// 249 | /// - ##### `#[deluxe(alias = ident)]` 250 | /// 251 | /// Parse the field with the given `ident`, or its Rust name. Can be repeated multiple times to 252 | /// provide additional aliases. 253 | /// 254 | /// - ##### `#[deluxe(default)]` 255 | /// 256 | /// Initializes the field with the value of [`Default::default`] if the field is omitted. 257 | /// 258 | /// It is not necessary to use this on fields of type [`Option`] or [`Flag`](deluxe_core::Flag), 259 | /// or any other type that has a top-level [`#[deluxe(default)]`](#deluxedefault) on the type 260 | /// itself. 261 | /// 262 | /// - ##### `#[deluxe(default = expr)]` 263 | /// 264 | /// Initializes the field with the value of `expr` if the field is omitted. The expression will 265 | /// be evaluated every time it is needed to construct the field, and must evaluate to a value of 266 | /// the same type as the field. 267 | /// 268 | /// - ##### `#[deluxe(flatten)]` 269 | /// 270 | /// Flattens the field so that its fields are parsed inline as part of the current struct or enum 271 | /// variant. 272 | /// 273 | /// When the container uses named fields, only enums or other structs with named fields can be 274 | /// flattened. The fields from the flattened field can be freely interspersed with the fields 275 | /// from the containing struct or variant. This has the effect of making it so the order of 276 | /// flattened fields does not matter when using named fields. 277 | /// 278 | /// When the container uses unnamed fields, only unnamed structs, tuples, and collections/arrays 279 | /// can be flattened. The order of flattened unnamed fields is important. The fields of the 280 | /// flattened structure will be consumed starting from the position of the field in the 281 | /// containing tuple. Flattening a collection into a tuple struct/variant without a finite size 282 | /// will consume all fields from that position until the end. 283 | /// 284 | /// This attribute is implemented by either calling 285 | /// [`ParseMetaFlatUnnamed::parse_meta_flat_unnamed`](deluxe_core::ParseMetaFlatUnnamed::parse_meta_flat_unnamed) 286 | /// or 287 | /// [`ParseMetaFlatNamed::parse_meta_flat_named`](deluxe_core::ParseMetaFlatNamed::parse_meta_flat_named) 288 | /// depending on the type of the containing structure. The appropriate trait will be 289 | /// automatically implemented when deriving [`ParseMetaItem`], but some implementations are 290 | /// provided for common collection types. Custom container types can support flattening by 291 | /// providing implementations of those traits. 292 | /// 293 | /// - ##### `#[deluxe(flatten(prefix = path)])` 294 | /// 295 | /// Flattens the field so that its fields are parsed inline as part of the current struct or enum 296 | /// variant, only accepting fields that are prefixed with `path`. This can be used if the 297 | /// flattened structure contains field names that conflict with the fields in the containing 298 | /// structure. 299 | /// 300 | /// For all other details on this attribute, refer to [`flatten`](#deluxeflatten-1). 301 | /// 302 | /// - ##### `#[deluxe(append)]` 303 | /// 304 | /// Allows duplicates of this field. Additional fields parsed with the same name will be appended 305 | /// on to the previous value. This attribute is only allowed on named fields. 306 | /// 307 | /// This attribute is implemented by calling 308 | /// [`ParseMetaAppend::parse_meta_append`](deluxe_core::ParseMetaAppend::parse_meta_append). Some 309 | /// implementations are provided for common collection types. Custom container types can support 310 | /// appending by providing an implementation of that trait. 311 | /// 312 | /// - ##### `#[deluxe(rest)]` 313 | /// 314 | /// Inserts all unknown fields into this field. Typically, this field will be a map type with 315 | /// [`syn::Path`] as the key. This attribute is only allowed on named fields. 316 | /// 317 | /// This attribute is implemented by calling 318 | /// [`ParseMetaRest::parse_meta_rest`](deluxe_core::ParseMetaRest::parse_meta_rest). Some 319 | /// implementations are provided for common collection types. Custom map types can be allowed as 320 | /// a rest field by providing an implementation of that trait. 321 | /// 322 | /// - ##### `#[deluxe(map = expr)]` 323 | /// 324 | /// `#[deluxe(and_then = expr)]` 325 | /// 326 | /// Executes additional functions ater parsing to perform additional transformations or 327 | /// validation on the input. 328 | /// 329 | /// These attributes are simple wrappers around [`Result::map`](deluxe_core::Result::map) and 330 | /// [`Result::and_then`](deluxe_core::Result::and_then). These attributes can be specified 331 | /// multiple times. When multiple are present, Deluxe will chain each function in the order the 332 | /// attributes were specified. 333 | /// 334 | /// For `map`, the function returned by `expr` must conform to the signature `fn(T) -> U`. For 335 | /// `and_then`, the function returned by `expr` must conform to the signature fn(T) -> 336 | /// [deluxe::Result](deluxe_core::Result)<U>. Returning 337 | /// [`Err`](deluxe_core::Result::Err) will cause the entire parse to fail. Arbitrary types can be 338 | /// used for `T` and `U` as long as the following constraints hold: 339 | /// 340 | /// - The first function must have a fully specified type for `T`, which will have its 341 | /// [`ParseMetaItem`](deluxe_core::ParseMetaItem) implementation used. 342 | /// - The `U from any function in the chain matches the `T` for the following function. 343 | /// - The last function must have a type for `U` that matches the type of the field. 344 | /// 345 | /// ##### Example 346 | /// 347 | /// ```ignore 348 | /// #[derive(deluxe::ParseMetaItem)] 349 | /// struct Data { 350 | /// // parses as an Ident but stored as a string 351 | /// #[deluxe(map = |i: syn::Ident| i.to_string())] 352 | /// ident_string: String, 353 | /// // converts an Ident to a string and does a validation 354 | /// #[deluxe(and_then = Self::check_ident)] 355 | /// valid_ident_string: String, 356 | /// } 357 | /// 358 | /// impl Data { 359 | /// fn check_ident(i: syn::Ident) -> deluxe::Result { 360 | /// let s = i.to_string(); 361 | /// if s == "invalid" { 362 | /// Err(syn::Error::new(i.span(), "`invalid` not allowed")) 363 | /// } else { 364 | /// Ok(s) 365 | /// } 366 | /// } 367 | /// } 368 | /// ``` 369 | /// 370 | /// - ##### `#[deluxe(with = module)]` 371 | /// 372 | /// When parsing, call functions from the path `module` instead of attempting to call 373 | /// [`ParseMetaItem`](deluxe_core::ParseMetaItem) functions. The path can be a module path or a 374 | /// path to a type containing associated functions. 375 | /// 376 | /// The functions will be called as `module::parse_meta_item`, `module::parse_meta_item_inline`, 377 | /// `module::parse_meta_item_flag`, `module::parse_meta_item_named`, and 378 | /// `module::missing_meta_item`. All five functions must be implemented, even if just to return 379 | /// an error. The signatures of these functions should match the equivalent functions in 380 | /// [`ParseMetaItem`](crate::ParseMetaItem), although they can be generic over the return type. 381 | /// Fields using this attribute are not required to implement 382 | /// [`ParseMetaItem`](crate::ParseMetaItem). 383 | /// 384 | /// `parse_meta_item_inline` implementations can call 385 | /// [`parse_first`](deluxe_core::parse_helpers::parse_first) to simply delegate the impementation 386 | /// to `parse_meta_item`. `parse_meta_item_flag` implementations can call 387 | /// [`flag_disallowed_error`](deluxe_core::parse_helpers::flag_disallowed_error) for a standard 388 | /// error if flags are not supported by the target type. `parse_meta_item_named` implementations 389 | /// can call [`parse_named_meta_item_with!`](deluxe_core::parse_named_meta_item_with) using 390 | /// `self` as the last parameter for the standard behavior. 391 | /// 392 | /// Some common parsers are available in the [`with`](deluxe_core::with) module. 393 | /// 394 | /// - ##### `#[deluxe(skip)]` 395 | /// 396 | /// Skips this field from parsing entirely. The field still must receive a default value through 397 | /// a `default` attribute either on the struct or the field, so the parse function can still 398 | /// construct the object. If not used in a struct with [`default`](#deluxedefault), then this 399 | /// implies [`default`](#deluxedefault-1) on the field if it is omitted. 400 | #[proc_macro_derive(ParseMetaItem, attributes(deluxe))] 401 | pub fn derive_parse_meta_item(item: TokenStream) -> TokenStream { 402 | let errors = Errors::new(); 403 | let mut tokens = util::parse::(item, &errors) 404 | .map(|input| parse_meta_item::impl_parse_meta_item(input, &errors)) 405 | .unwrap_or_default(); 406 | tokens.extend(errors.into_compile_errors()); 407 | tokens.into() 408 | } 409 | -------------------------------------------------------------------------------- /macros/parse_attributes.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use deluxe_core::Errors; 4 | use proc_macro2::{Span, TokenStream}; 5 | 6 | use crate::types::*; 7 | 8 | #[derive(PartialEq, Eq, Clone, Copy)] 9 | pub enum Mode { 10 | Parse, 11 | Extract, 12 | } 13 | 14 | impl Mode { 15 | fn into_token_mode(self) -> TokenMode { 16 | match self { 17 | Self::Parse => TokenMode::ParseAttributes, 18 | Self::Extract => TokenMode::ExtractAttributes, 19 | } 20 | } 21 | } 22 | 23 | struct AttrImpl<'i> { 24 | pub parse: TokenStream, 25 | pub crate_path: syn::Path, 26 | pub priv_path: syn::Path, 27 | pub attributes: Vec, 28 | pub container_field: Option<&'i syn::Field>, 29 | pub container_lifetime: Option, 30 | pub container_ty: Option, 31 | } 32 | 33 | #[inline] 34 | fn impl_for_struct<'i>( 35 | input: &'i syn::DeriveInput, 36 | struct_: &syn::DataStruct, 37 | mode: Mode, 38 | errors: &Errors, 39 | ) -> Option> { 40 | let struct_attr = errors.push_result(>::parse_attributes(input)); 43 | 44 | let crate_path = super::get_crate_path(struct_attr.as_ref().map(|s| s.crate_.clone()), errors)?; 45 | let crate_ = &crate_path; 46 | let priv_path: syn::Path = syn::parse_quote! { #crate_::____private }; 47 | let priv_ = &priv_path; 48 | 49 | let parse = struct_attr 50 | .as_ref() 51 | .map(|s| { 52 | let ItemDef { inline, .. } = s.to_parsing_tokens( 53 | input, 54 | crate_, 55 | mode.into_token_mode(), 56 | &parse_quote_mixed! { inline(input) }, 57 | &parse_quote_mixed! { allowed }, 58 | ); 59 | let pre = match &struct_.fields { 60 | syn::Fields::Named(_) => { 61 | let field_names = struct_attr 62 | .as_ref() 63 | .map(|s| s.to_field_names_tokens(crate_, priv_)) 64 | .unwrap_or_else(|| quote_mixed! { &[] }); 65 | let accepts_all = struct_attr 66 | .as_ref() 67 | .and_then(|s| s.to_accepts_all_tokens(crate_)) 68 | .unwrap_or_else(|| quote_mixed! { false }); 69 | quote_mixed! { 70 | let allowed = #field_names; 71 | let validate = !(#accepts_all); 72 | let prefix = ""; 73 | } 74 | } 75 | syn::Fields::Unnamed(_) => { 76 | quote_mixed! { 77 | let mut index = 0usize; 78 | } 79 | } 80 | _ => quote::quote! {}, 81 | }; 82 | quote_mixed! { 83 | #pre 84 | #inline 85 | } 86 | }) 87 | .unwrap_or_else(|| { 88 | quote_mixed! { 89 | #priv_::unreachable!() 90 | } 91 | }); 92 | let (container_field, container_lifetime, container_ty) = struct_attr 93 | .as_ref() 94 | .map(|s| s.fields.as_slice()) 95 | .unwrap_or_default() 96 | .iter() 97 | .find_map(|f| { 98 | f.container 99 | .as_ref() 100 | .map(|c| (Some(f.field), c.lifetime.clone(), c.ty.clone())) 101 | }) 102 | .unwrap_or_default(); 103 | Some(AttrImpl { 104 | parse, 105 | crate_path, 106 | priv_path, 107 | attributes: struct_attr.map(|s| s.attributes).unwrap_or_default(), 108 | container_field, 109 | container_lifetime, 110 | container_ty, 111 | }) 112 | } 113 | 114 | #[inline] 115 | fn impl_for_enum<'i>( 116 | input: &'i syn::DeriveInput, 117 | mode: Mode, 118 | errors: &Errors, 119 | ) -> Option> { 120 | let enum_attr = errors.push_result( 121 | >::parse_attributes(input), 122 | ); 123 | 124 | let crate_path = super::get_crate_path(enum_attr.as_ref().map(|e| e.crate_.clone()), errors)?; 125 | let crate_ = &crate_path; 126 | let priv_path: syn::Path = syn::parse_quote! { #crate_::____private }; 127 | let priv_ = &priv_path; 128 | 129 | let parse = enum_attr 130 | .as_ref() 131 | .map(|e| { 132 | let parse = e.to_inline_parsing_tokens(crate_, mode.into_token_mode()); 133 | let field_names = e.to_field_names_tokens(crate_, priv_); 134 | let accepts_all = e 135 | .to_accepts_all_tokens(crate_) 136 | .unwrap_or_else(|| quote_mixed! { false }); 137 | quote_mixed! { 138 | let allowed = #field_names; 139 | let validate = !(#accepts_all); 140 | let prefix = ""; 141 | #parse 142 | } 143 | }) 144 | .unwrap_or_else(|| { 145 | quote_mixed! { 146 | #priv_::unreachable!() 147 | } 148 | }); 149 | 150 | let (container_field, container_lifetime, container_ty) = enum_attr 151 | .as_ref() 152 | .map(|e| e.variants.as_slice()) 153 | .unwrap_or_default() 154 | .iter() 155 | .find_map(|v| { 156 | v.fields.iter().find_map(|f| { 157 | f.container 158 | .as_ref() 159 | .map(|c| (Some(f.field), c.lifetime.clone(), c.ty.clone())) 160 | }) 161 | }) 162 | .unwrap_or_default(); 163 | Some(AttrImpl { 164 | parse, 165 | crate_path, 166 | priv_path, 167 | attributes: enum_attr.map(|s| s.attributes).unwrap_or_default(), 168 | container_field, 169 | container_lifetime, 170 | container_ty, 171 | }) 172 | } 173 | 174 | pub fn impl_parse_attributes(input: syn::DeriveInput, errors: &Errors, mode: Mode) -> TokenStream { 175 | let attr = match &input.data { 176 | syn::Data::Struct(struct_) => impl_for_struct(&input, struct_, mode, errors), 177 | syn::Data::Enum(_) => impl_for_enum(&input, mode, errors), 178 | syn::Data::Union(union_) => { 179 | errors.push_spanned( 180 | union_.union_token, 181 | match mode { 182 | Mode::Parse => "union not supported with derive(ParseAttributes)", 183 | Mode::Extract => "union not supported with derive(ExtractAttributes)", 184 | }, 185 | ); 186 | return Default::default(); 187 | } 188 | }; 189 | let AttrImpl { 190 | parse, 191 | crate_path: crate_, 192 | priv_path: priv_, 193 | attributes, 194 | container_field, 195 | mut container_lifetime, 196 | container_ty, 197 | } = match attr { 198 | Some(a) => a, 199 | None => return Default::default(), 200 | }; 201 | 202 | let ident = &input.ident; 203 | let mut generics = input.generics.clone(); 204 | 205 | let mut container_is_generic = false; 206 | let mut container_is_ref = false; 207 | let mut container_ty = container_ty.map(Cow::Owned); 208 | if let Some(container_field) = container_field { 209 | let mut ty = &container_field.ty; 210 | // try to guess some things about the container type. 211 | // first infer if this is an option, and if so, use its inner type 212 | if_chain::if_chain! { 213 | if let syn::Type::Path(path) = ty; 214 | if path.qself.is_none(); 215 | if let Some(last) = path.path.segments.last(); 216 | if last.ident == "Option"; 217 | if let syn::PathArguments::AngleBracketed(args) = &last.arguments; 218 | if args.args.len() == 1; 219 | if let syn::GenericArgument::Type(t) = &args.args[0]; 220 | then { 221 | ty = t; 222 | } 223 | } 224 | // use inner type and lifetime from reference 225 | if let syn::Type::Reference(ref_) = ty { 226 | if container_lifetime.is_none() { 227 | container_lifetime = ref_.lifetime.clone(); 228 | } 229 | container_is_ref = true; 230 | ty = &*ref_.elem; 231 | } 232 | // if we still need a lifetime, and inner type has a lifetime, use that 233 | if_chain::if_chain! { 234 | if container_lifetime.is_none(); 235 | if let syn::Type::Path(path) = ty; 236 | if let Some(last) = path.path.segments.last(); 237 | if let syn::PathArguments::AngleBracketed(args) = &last.arguments; 238 | if let Some(syn::GenericArgument::Lifetime(lt)) = args.args.first(); 239 | then { 240 | container_lifetime = Some(lt.clone()); 241 | } 242 | } 243 | // see if the type matches a generic param 244 | if container_ty.is_none() { 245 | container_ty = Some(Cow::Borrowed(ty)); 246 | if_chain::if_chain! { 247 | if let syn::Type::Path(path) = ty; 248 | if path.qself.is_none(); 249 | if let Some(ident) = path.path.get_ident(); 250 | if generics.type_params().any(|p| p.ident == *ident); 251 | then { 252 | container_is_generic = true; 253 | } 254 | } 255 | } 256 | } 257 | // if there was no container field, make it generic and add our own type param 258 | let container_ty = container_ty.unwrap_or_else(|| { 259 | container_is_generic = true; 260 | let mut ty = String::from("T"); 261 | // ensure the `T` is a unique ident 262 | while generics.type_params().any(|p| p.ident == ty) { 263 | ty.push('_'); 264 | } 265 | let ty = syn::Ident::new(&ty, Span::mixed_site()); 266 | generics.params.insert(0, syn::parse_quote! { #ty }); 267 | Cow::Owned(parse_quote_mixed! { #ty }) 268 | }); 269 | 270 | // value types must be copied into the structure 271 | if container_field.is_some() && !container_is_ref { 272 | let where_clause = generics.make_where_clause(); 273 | where_clause.predicates.push(syn::parse_quote! { 274 | #container_ty: #priv_::Clone 275 | }); 276 | } 277 | 278 | // must be able to access attributes on the generic type 279 | if container_is_generic { 280 | let where_clause = generics.make_where_clause(); 281 | where_clause.predicates.push(syn::parse_quote! { 282 | #container_ty: #crate_::HasAttributes 283 | }); 284 | } 285 | 286 | // ParseAttributes needs a lifetime param since the attributes are always referenced 287 | if mode == Mode::Parse && container_lifetime.is_none() { 288 | let mut lt = String::from("t"); 289 | while generics.lifetimes().any(|l| l.lifetime.ident == lt) { 290 | lt.push('_'); 291 | } 292 | lt.insert(0, '\''); 293 | container_lifetime = Some(syn::Lifetime::new(<, Span::mixed_site())); 294 | generics 295 | .params 296 | .insert(0, syn::parse_quote! { #container_lifetime }); 297 | } 298 | 299 | let (_, type_generics, _) = input.generics.split_for_impl(); 300 | let (impl_generics, _, where_clause) = generics.split_for_impl(); 301 | 302 | let matches = if attributes.is_empty() { 303 | quote_mixed! { true } 304 | } else { 305 | let matches = attributes.iter().map(|p| { 306 | let segs = p.segments.iter().map(|s| s.ident.to_string()); 307 | quote_mixed! { &[#(#segs),*] } 308 | }); 309 | quote_mixed! { 310 | #(#priv_::parse_helpers::path_matches(path, #matches))||* 311 | } 312 | }; 313 | 314 | let sig = match mode { 315 | Mode::Parse => quote_mixed! { 316 | fn parse_attributes(obj: &#container_lifetime #container_ty) -> #crate_::Result 317 | }, 318 | Mode::Extract => quote_mixed! { 319 | fn extract_attributes(obj: &mut #container_ty) -> #crate_::Result 320 | }, 321 | }; 322 | let trait_ = match mode { 323 | Mode::Parse => quote_mixed! { 324 | #crate_::ParseAttributes<#container_lifetime, #container_ty> 325 | }, 326 | Mode::Extract => quote_mixed! { 327 | #crate_::ExtractAttributes<#container_ty> 328 | }, 329 | }; 330 | let get_tokens = match mode { 331 | Mode::Parse => quote_mixed! { ref_tokens }, 332 | Mode::Extract => quote_mixed! { take_tokens }, 333 | }; 334 | let tokens_try = match mode { 335 | Mode::Parse => None, 336 | Mode::Extract => Some(quote_mixed! { ? }), 337 | }; 338 | let path_name_unwrap = attributes.first().map(|path| { 339 | let path = deluxe_core::parse_helpers::key_to_string(path); 340 | quote_mixed! { .or(#priv_::Option::Some(#path)) } 341 | }); 342 | 343 | quote_mixed! { 344 | impl #impl_generics #trait_ for #ident #type_generics #where_clause { 345 | #[inline] 346 | fn path_matches(path: &#priv_::Path) -> #priv_::bool { 347 | #matches 348 | } 349 | #sig { 350 | #priv_::parse_helpers::parse_struct_attr_tokens( 351 | #priv_::parse_helpers::#get_tokens::(obj) #tokens_try, 352 | |inputs, spans| { 353 | let span = #priv_::parse_helpers::first_span(spans); 354 | let path_name = #priv_::parse_helpers::first_path_name(spans) #path_name_unwrap; 355 | let _mode = #crate_::ParseMode::Named(span); 356 | #parse 357 | } 358 | ) 359 | } 360 | } 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /macros/parse_meta_item.rs: -------------------------------------------------------------------------------- 1 | use deluxe_core::Errors; 2 | use proc_macro2::{Span, TokenStream}; 3 | use quote::{quote, quote_spanned, ToTokens}; 4 | use syn::spanned::Spanned; 5 | 6 | use crate::types::*; 7 | 8 | struct MetaDef { 9 | pub parse: TokenStream, 10 | pub inline: Option, 11 | pub flag: Option, 12 | pub default: Option, 13 | pub extra: Option, 14 | pub crate_path: syn::Path, 15 | pub priv_path: syn::Path, 16 | } 17 | 18 | #[inline] 19 | fn impl_for_struct( 20 | input: &syn::DeriveInput, 21 | struct_: &syn::DataStruct, 22 | errors: &Errors, 23 | ) -> Option { 24 | let mut struct_attr = errors.push_result(>::parse_attributes(input)); 27 | 28 | let crate_path = super::get_crate_path(struct_attr.as_ref().map(|s| s.crate_.clone()), errors)?; 29 | let crate_ = &crate_path; 30 | let priv_path: syn::Path = syn::parse_quote! { #crate_::____private }; 31 | let priv_ = &priv_path; 32 | 33 | let any_flat = struct_attr 34 | .as_ref() 35 | .map(|s| s.fields.iter().any(|f| f.is_flat())) 36 | .unwrap_or(false); 37 | let (parse, parse_flat, inline, flag, field_names, mut extra) = struct_attr 38 | .as_mut() 39 | .map(|struct_attr| { 40 | for f in &mut struct_attr.fields { 41 | if let Some(span) = f.container.as_ref().and_then(|c| c.value.then_some(c.span)) { 42 | if f.default.is_none() { 43 | f.default = Some(FieldDefault::Default(span)); 44 | } 45 | } 46 | } 47 | 48 | let fields = struct_attr.fields.as_slice(); 49 | 50 | let make_inline_expr = |inputs_expr: TokenStream| -> TokenStream { 51 | match &struct_.fields { 52 | syn::Fields::Named(_) => quote_mixed! { 53 | ::parse_meta_flat_named( 54 | #inputs_expr, 55 | _mode, 56 | "", 57 | !::ACCEPTS_ALL, 58 | ) 59 | }, 60 | syn::Fields::Unnamed(_) => quote_mixed! { 61 | ::parse_meta_flat_unnamed(#inputs_expr, _mode, 0) 62 | }, 63 | syn::Fields::Unit => quote_mixed! { 64 | ::parse_meta_item_inline( 65 | #inputs_expr, #crate_::ParseMode::Unnamed, 66 | ) 67 | }, 68 | } 69 | }; 70 | 71 | let ItemDef { 72 | parse, 73 | inline, 74 | flag, 75 | extra_traits, 76 | } = struct_attr.to_parsing_tokens( 77 | input, 78 | crate_, 79 | TokenMode::ParseMetaItem, 80 | &make_inline_expr(quote_mixed! { &[input] }), 81 | &parse_quote_mixed! { 82 | ::field_names() 83 | }, 84 | ); 85 | let (parse_flat, inline) = 86 | if struct_attr.is_transparent() || matches!(&struct_.fields, syn::Fields::Unit) { 87 | (None, inline) 88 | } else { 89 | (inline, Some(make_inline_expr(quote_mixed! { inputs }).into_token_stream())) 90 | }; 91 | let field_names = match &struct_.fields { 92 | syn::Fields::Named(_) => Some(struct_attr.to_field_names_tokens(crate_, priv_)), 93 | syn::Fields::Unnamed(_) => { 94 | let field_count = fields.iter().filter(|f| !f.is_flat()).count(); 95 | let extra_counts = fields.iter().filter_map(|f| { 96 | if !f.is_flat() { 97 | return None; 98 | } 99 | let ty = &f.field.ty; 100 | Some(quote_spanned! { ty.span() => 101 | <#ty as #crate_::ParseMetaFlatUnnamed>::field_count().unwrap_or(0) 102 | }) 103 | }); 104 | Some(quote_mixed! { 105 | #priv_::Option::Some(#field_count #( + #extra_counts)*) 106 | }) 107 | } 108 | _ => None, 109 | }; 110 | (parse, parse_flat, inline, flag, field_names, extra_traits) 111 | }) 112 | .unwrap_or_else(|| { 113 | let fail = quote_mixed! { 114 | #priv_::unreachable!() 115 | }; 116 | ( 117 | fail.clone(), 118 | Some(fail.clone()), 119 | Some(fail.clone()), 120 | Some(fail.clone()), 121 | Some(fail), 122 | None, 123 | ) 124 | }); 125 | 126 | match (&struct_.fields, parse_flat) { 127 | (syn::Fields::Named(_), Some(parse_flat)) => { 128 | let struct_ident = &input.ident; 129 | let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); 130 | 131 | let accepts_all = struct_attr 132 | .as_ref() 133 | .and_then(|s| s.to_accepts_all_tokens(crate_)) 134 | .into_iter(); 135 | extra.get_or_insert_with(TokenStream::new).extend(quote_mixed! { 136 | impl #impl_generics #crate_::ParseMetaFlatNamed for #struct_ident #type_generics #where_clause { 137 | #(const ACCEPTS_ALL: #priv_::bool = #accepts_all;)* 138 | #[inline] 139 | fn field_names() -> &'static [&'static #priv_::str] { 140 | #field_names 141 | } 142 | #[inline] 143 | fn parse_meta_flat_named<'____s, ____S>( 144 | inputs: &[____S], 145 | _mode: #crate_::ParseMode, 146 | prefix: &#priv_::str, 147 | validate: #priv_::bool, 148 | ) -> #crate_::Result 149 | where 150 | ____S: #priv_::Borrow<#priv_::ParseBuffer<'____s>>, 151 | { 152 | let span = _mode.to_full_span(inputs); 153 | #parse_flat 154 | } 155 | } 156 | 157 | impl #impl_generics #crate_::ParseMetaFlatUnnamed for #struct_ident #type_generics #where_clause { 158 | #[inline] 159 | fn field_count() -> #priv_::Option<#priv_::usize> { 160 | #priv_::Option::None 161 | } 162 | #[inline] 163 | fn parse_meta_flat_unnamed<'____s, ____S>( 164 | inputs: &[____S], 165 | mode: #crate_::ParseMode, 166 | _index: #priv_::usize, 167 | ) -> #crate_::Result 168 | where 169 | ____S: #priv_::Borrow<#priv_::ParseBuffer<'____s>>, 170 | { 171 | ::parse_meta_flat_named(inputs, mode, "", true) 172 | } 173 | } 174 | }); 175 | } 176 | (syn::Fields::Unnamed(_), Some(parse_flat)) => { 177 | let struct_ident = &input.ident; 178 | let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); 179 | let index_mut = any_flat.then(|| quote! { mut }); 180 | extra.get_or_insert_with(TokenStream::new).extend(quote_mixed! { 181 | impl #impl_generics #crate_::ParseMetaFlatUnnamed for #struct_ident #type_generics #where_clause { 182 | #[inline] 183 | fn field_count() -> #priv_::Option<#priv_::usize> { 184 | #field_names 185 | } 186 | #[inline] 187 | fn parse_meta_flat_unnamed<'____s, ____S>( 188 | inputs: &[____S], 189 | _mode: #crate_::ParseMode, 190 | #index_mut index: #priv_::usize, 191 | ) -> #crate_::Result 192 | where 193 | ____S: #priv_::Borrow<#priv_::ParseBuffer<'____s>>, 194 | { 195 | #parse_flat 196 | } 197 | } 198 | }); 199 | } 200 | _ => {} 201 | } 202 | let default = struct_attr.and_then(|s| s.default).map(|d| { 203 | d.to_expr(Some(&syn::parse_quote_spanned! { d.span() => Self }), priv_) 204 | .into_owned() 205 | }); 206 | Some(MetaDef { 207 | parse, 208 | inline, 209 | flag, 210 | default, 211 | extra, 212 | crate_path, 213 | priv_path, 214 | }) 215 | } 216 | 217 | #[inline] 218 | fn impl_for_enum(input: &syn::DeriveInput, errors: &Errors) -> Option { 219 | let enum_attr = errors.push_result( 220 | >::parse_attributes(input), 221 | ); 222 | 223 | let crate_path = super::get_crate_path(enum_attr.as_ref().map(|e| e.crate_.clone()), errors)?; 224 | let crate_ = &crate_path; 225 | let priv_path: syn::Path = syn::parse_quote! { #crate_::____private }; 226 | let priv_ = &priv_path; 227 | 228 | let enum_ident = &input.ident; 229 | let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); 230 | let (parse, field_names) = enum_attr 231 | .as_ref() 232 | .map(|e| { 233 | for v in &e.variants { 234 | for f in &v.fields { 235 | if let Some(container) = f.container.as_ref() { 236 | if f.is_container() && f.default.is_none() { 237 | errors.push( 238 | container.span(), 239 | "derive(ParseMetaItem) requires container field to have `default`", 240 | ); 241 | } 242 | } 243 | } 244 | } 245 | let parse = e.to_inline_parsing_tokens(crate_, TokenMode::ParseMetaItem); 246 | let field_names = e.to_field_names_tokens(crate_, priv_); 247 | ( 248 | quote_mixed! { 249 | let allowed = ::field_names(); 250 | #parse 251 | }, 252 | field_names, 253 | ) 254 | }) 255 | .unwrap_or_else(|| { 256 | let fail = quote_mixed! { 257 | #priv_::unreachable!() 258 | }; 259 | (fail.clone(), fail) 260 | }); 261 | Some(MetaDef { 262 | parse: quote_mixed! { 263 | <#priv_::parse_helpers::Brace as #priv_::parse_helpers::ParseDelimited>::parse_delimited_meta_item( 264 | input, _mode.to_named(input), 265 | ) 266 | }, 267 | inline: Some(quote_mixed! { 268 | ::parse_meta_flat_named( 269 | inputs, 270 | _mode, 271 | "", 272 | !::ACCEPTS_ALL, 273 | ) 274 | }), 275 | flag: Some(quote_mixed! { 276 | #priv_::parse_helpers::parse_empty_meta_item(span, #crate_::ParseMode::Named(span)) 277 | }), 278 | default: enum_attr.and_then(|s| s.default).map(|d| { 279 | d.to_expr(Some(&syn::parse_quote_spanned! { d.span() => Self }), priv_) 280 | .into_owned() 281 | }), 282 | extra: Some(quote_mixed! { 283 | impl #impl_generics #crate_::ParseMetaFlatNamed for #enum_ident #type_generics #where_clause { 284 | #[inline] 285 | fn field_names() -> &'static [&'static #priv_::str] { 286 | #field_names 287 | } 288 | #[inline] 289 | fn parse_meta_flat_named<'____s, ____S>( 290 | inputs: &[____S], 291 | _mode: #crate_::ParseMode, 292 | prefix: &#priv_::str, 293 | validate: #priv_::bool, 294 | ) -> #crate_::Result 295 | where 296 | ____S: #priv_::Borrow<#priv_::ParseBuffer<'____s>>, 297 | { 298 | let span = _mode.to_full_span(inputs); 299 | #parse 300 | } 301 | } 302 | 303 | impl #impl_generics #crate_::ParseMetaFlatUnnamed for #enum_ident #type_generics #where_clause { 304 | #[inline] 305 | fn field_count() -> #priv_::Option<#priv_::usize> { 306 | #priv_::Option::None 307 | } 308 | #[inline] 309 | fn parse_meta_flat_unnamed<'____s, ____S>( 310 | inputs: &[____S], 311 | mode: #crate_::ParseMode, 312 | _index: #priv_::usize, 313 | ) -> #crate_::Result 314 | where 315 | ____S: #priv_::Borrow<#priv_::ParseBuffer<'____s>>, 316 | { 317 | ::parse_meta_flat_named(inputs, mode, "", true) 318 | } 319 | } 320 | }), 321 | crate_path, 322 | priv_path, 323 | }) 324 | } 325 | 326 | pub fn impl_parse_meta_item(input: syn::DeriveInput, errors: &Errors) -> TokenStream { 327 | let meta = match &input.data { 328 | syn::Data::Struct(struct_) => impl_for_struct(&input, struct_, errors), 329 | syn::Data::Enum(_) => impl_for_enum(&input, errors), 330 | syn::Data::Union(union) => { 331 | errors.push_spanned( 332 | union.union_token, 333 | "union not supported with derive(ParseMetaItem)", 334 | ); 335 | return Default::default(); 336 | } 337 | }; 338 | let MetaDef { 339 | parse, 340 | inline, 341 | flag, 342 | default, 343 | extra, 344 | crate_path: crate_, 345 | priv_path: priv_, 346 | } = match meta { 347 | Some(m) => m, 348 | None => return Default::default(), 349 | }; 350 | 351 | let ident = &input.ident; 352 | let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); 353 | 354 | let inline = inline.map(|inline| { 355 | quote_mixed! { 356 | #[inline] 357 | fn parse_meta_item_inline<'____s, ____S>( 358 | inputs: &[____S], 359 | _mode: #crate_::ParseMode, 360 | ) -> #crate_::Result 361 | where 362 | ____S: #priv_::Borrow<#priv_::ParseBuffer<'____s>>, 363 | { 364 | #inline 365 | } 366 | } 367 | }); 368 | let flag = flag.map(|flag| { 369 | quote_mixed! { 370 | #[inline] 371 | fn parse_meta_item_flag(span: #priv_::Span) -> #crate_::Result { 372 | #flag 373 | } 374 | } 375 | }); 376 | let missing = default.map(|default| { 377 | quote_mixed! { 378 | #[inline] 379 | fn missing_meta_item(name: &#priv_::str, span: #priv_::Span) -> #crate_::Result { 380 | #crate_::Result::Ok((#default)) 381 | } 382 | } 383 | }); 384 | quote_mixed! { 385 | impl #impl_generics #crate_::ParseMetaItem for #ident #type_generics #where_clause { 386 | #[inline] 387 | fn parse_meta_item( 388 | input: #priv_::ParseStream, 389 | _mode: #crate_::ParseMode, 390 | ) -> #crate_::Result { 391 | #parse 392 | } 393 | #inline 394 | #flag 395 | #missing 396 | } 397 | #extra 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /macros/types.rs: -------------------------------------------------------------------------------- 1 | mod field; 2 | pub use field::*; 3 | mod r#struct; 4 | pub use r#struct::*; 5 | mod variant; 6 | pub use variant::*; 7 | mod r#enum; 8 | pub use r#enum::*; 9 | -------------------------------------------------------------------------------- /macros/types/enum.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use deluxe_core::{ 3 | parse_helpers::{self, FieldStatus}, 4 | ParseAttributes, ParseMetaItem, Result, 5 | }; 6 | use proc_macro2::{Span, TokenStream}; 7 | use quote::quote_spanned; 8 | use std::collections::{BTreeSet, HashSet}; 9 | use syn::spanned::Spanned; 10 | 11 | pub struct Enum<'e> { 12 | pub enum_: &'e syn::DataEnum, 13 | pub variants: Vec>, 14 | pub default: Option, 15 | pub crate_: Option, 16 | pub attributes: Vec, 17 | pub and_thens: Vec, 18 | pub allow_unknown_fields: Option, 19 | } 20 | 21 | impl<'e> Enum<'e> { 22 | #[inline] 23 | pub fn field_names() -> &'static [&'static str] { 24 | &["default", "crate", "attributes", "and_then"] 25 | } 26 | #[inline] 27 | pub fn to_inline_parsing_tokens(&self, crate_: &syn::Path, mode: TokenMode) -> TokenStream { 28 | let default = self.default.as_ref().map(|d| { 29 | let priv_path: syn::Path = syn::parse_quote! { #crate_::____private }; 30 | d.to_expr( 31 | Some(&syn::parse_quote_spanned! { d.span() => Self }), 32 | &priv_path, 33 | ) 34 | }); 35 | Variant::to_parsing_tokens( 36 | &self.variants, 37 | crate_, 38 | mode, 39 | default.as_ref().map(|d| d.as_ref()), 40 | &self.and_thens, 41 | self.allow_unknown_fields.unwrap_or(false), 42 | ) 43 | } 44 | pub fn to_accepts_all_tokens(&self, crate_: &syn::Path) -> Option { 45 | if self.allow_unknown_fields.unwrap_or(false) 46 | || self.variants.iter().any(|v| { 47 | !v.is_skipped() 48 | && v.flatten.unwrap_or(false) 49 | && v.allow_unknown_fields.unwrap_or(false) 50 | }) 51 | { 52 | return Some(quote_mixed! { true }); 53 | } 54 | let mut accepts_all = self 55 | .variants 56 | .iter() 57 | .filter_map(|v| { 58 | v.flatten 59 | .unwrap_or(false) 60 | .then(|| Field::to_accepts_all_tokens(&v.fields, crate_)) 61 | .flatten() 62 | }) 63 | .peekable(); 64 | accepts_all.peek().is_some().then(|| { 65 | quote_mixed! { #(#accepts_all)||* } 66 | }) 67 | } 68 | pub fn to_field_names_tokens(&self, crate_: &syn::Path, priv_: &syn::Path) -> TokenStream { 69 | let any_flat_nested = self 70 | .variants 71 | .iter() 72 | .any(|v| v.flatten.unwrap_or(false) && v.fields.iter().any(|f| f.is_flat())); 73 | let field_names = self.variants.iter().flat_map(|v| { 74 | v.idents 75 | .iter() 76 | .filter_map(|ident| { 77 | if v.flatten.unwrap_or(false) || v.is_skipped() { 78 | return None; 79 | } 80 | let ident = ident.to_string(); 81 | Some(if any_flat_nested { 82 | quote_mixed! { vec.push(#ident); } 83 | } else { 84 | quote_mixed! { #ident } 85 | }) 86 | }) 87 | .chain(v.fields.iter().filter_map(|field| { 88 | if !v.flatten.unwrap_or(false) { 89 | return None; 90 | } 91 | match &field.flatten { 92 | Some(FieldFlatten { 93 | value: true, 94 | prefix: Some(prefix), 95 | .. 96 | }) => { 97 | let ty = &field.field.ty; 98 | let prefix = parse_helpers::key_to_string(prefix); 99 | let names = quote_spanned! { ty.span() => 100 | <#ty as #crate_::ParseMetaFlatNamed>::field_names() 101 | }; 102 | Some(quote_mixed! { 103 | vec.extend({ 104 | static CELL: #priv_::SyncOnceCell<#priv_::Vec<#priv_::String>> = #priv_::SyncOnceCell::new(); 105 | CELL.get_or_init(|| #priv_::parse_helpers::join_paths(#prefix, #names)) 106 | .iter() 107 | .map(|ps| ps.as_str()) 108 | }); 109 | }) 110 | } 111 | Some(FieldFlatten { 112 | value: true, 113 | prefix: None, 114 | .. 115 | }) => { 116 | let ty = &field.field.ty; 117 | let names = quote_spanned! { ty.span() => 118 | <#ty as #crate_::ParseMetaFlatNamed>::field_names() 119 | }; 120 | Some(quote_mixed! { 121 | vec.extend_from_slice(#names); 122 | }) 123 | }, 124 | _ => None, 125 | } 126 | })) 127 | .chain(v.fields.iter().flat_map(|field| { 128 | field.idents.iter().filter_map(|ident| { 129 | if !v.flatten.unwrap_or(false) { 130 | return None; 131 | } 132 | match &field.flatten { 133 | Some(FieldFlatten { 134 | value: true, 135 | .. 136 | }) => None, 137 | _ => { 138 | let ident = ident.to_string(); 139 | if any_flat_nested { 140 | Some(quote_mixed! { vec.push(#ident); }) 141 | } else { 142 | Some(quote_mixed! { #ident }) 143 | } 144 | } 145 | } 146 | }) 147 | })) 148 | }); 149 | if any_flat_nested { 150 | quote_mixed! { 151 | { 152 | static CELL: #priv_::SyncOnceCell<#priv_::Vec<&'static #priv_::str>> = #priv_::SyncOnceCell::new(); 153 | CELL.get_or_init(|| { 154 | let mut vec = #priv_::Vec::new(); 155 | #(#field_names)* 156 | vec 157 | }).as_slice() 158 | } 159 | } 160 | } else { 161 | quote_mixed! { 162 | &[#(#field_names),*] 163 | } 164 | } 165 | } 166 | } 167 | 168 | deluxe_core::define_with_collection!(mod mod_path_vec, deluxe_core::with::mod_path, Vec); 169 | 170 | impl<'e> ParseAttributes<'e, syn::DeriveInput> for Enum<'e> { 171 | #[inline] 172 | fn path_matches(path: &syn::Path) -> bool { 173 | path.is_ident("deluxe") 174 | } 175 | fn parse_attributes(i: &'e syn::DeriveInput) -> Result { 176 | parse_helpers::parse_struct_attr_tokens( 177 | parse_helpers::ref_tokens::(i), 178 | |inputs, _| { 179 | let enum_ = match &i.data { 180 | syn::Data::Enum(e) => e, 181 | _ => return Err(syn::Error::new_spanned(i, "wrong DeriveInput type")), 182 | }; 183 | let errors = crate::Errors::new(); 184 | let mut default = FieldStatus::::None; 185 | let mut crate_ = FieldStatus::None; 186 | let mut and_thens = Vec::new(); 187 | let mut attributes = Vec::new(); 188 | let mut allow_unknown_fields = FieldStatus::None; 189 | errors.push_result(parse_helpers::parse_struct(inputs, |input, path, span| { 190 | match path { 191 | "default" => default.parse_named_item("default", input, span, &errors), 192 | "crate" => crate_.parse_named_item("crate", input, span, &errors), 193 | "and_then" => { 194 | match errors.push_result(<_>::parse_meta_item_named(input, path, span)) 195 | { 196 | Some(e) => and_thens.push(e), 197 | None => parse_helpers::skip_meta_item(input), 198 | } 199 | } 200 | "attributes" => { 201 | match errors 202 | .push_result(mod_path_vec::parse_meta_item_named(input, path, span)) 203 | { 204 | Some(attrs) => attributes.extend(attrs), 205 | None => parse_helpers::skip_meta_item(input), 206 | } 207 | } 208 | "allow_unknown_fields" => allow_unknown_fields.parse_named_item( 209 | "allow_unknown_fields", 210 | input, 211 | span, 212 | &errors, 213 | ), 214 | _ => { 215 | parse_helpers::check_unknown_attribute( 216 | path, 217 | span, 218 | Self::field_names(), 219 | &errors, 220 | ); 221 | parse_helpers::skip_meta_item(input); 222 | } 223 | } 224 | Ok(()) 225 | })); 226 | let variants = enum_ 227 | .variants 228 | .iter() 229 | .filter_map(|v| { 230 | errors.push_result( 231 | >::parse_attributes(v), 232 | ) 233 | }) 234 | .collect::>(); 235 | let mut all_idents = HashSet::new(); 236 | let mut container = None; 237 | let mut variant_keys = BTreeSet::>>::new(); 238 | for variant in &variants { 239 | if let Some(c) = variant 240 | .fields 241 | .iter() 242 | .find_map(|f| f.container.as_ref().and_then(|c| c.value.then_some(c))) 243 | { 244 | if container.is_some() { 245 | if let Some(lifetime) = c.lifetime.as_ref() { 246 | errors.push_spanned( 247 | lifetime, 248 | "only the first `container` field can contain a `lifetime` parameter" 249 | ); 250 | } 251 | if let Some(ty) = c.ty.as_ref() { 252 | errors.push_spanned( 253 | ty, 254 | "only the first `container` field can contain a `type` parameter", 255 | ); 256 | } 257 | } else { 258 | container = Some(c); 259 | } 260 | } 261 | if !variant.flatten.unwrap_or(false) { 262 | variant_keys.insert( 263 | [variant.idents.iter().map(|i| i.to_string()).collect()].into(), 264 | ); 265 | for ident in &variant.idents { 266 | if all_idents.contains(&ident) { 267 | errors.push_spanned( 268 | ident, 269 | format_args!("duplicate variant name for `{ident}`"), 270 | ); 271 | } else { 272 | all_idents.insert(ident); 273 | } 274 | } 275 | } 276 | } 277 | for variant in &variants { 278 | if variant.flatten.unwrap_or(false) { 279 | if matches!(variant.variant.fields, syn::Fields::Named(_)) { 280 | let key = variant.field_key(); 281 | if variant_keys.contains(&key) { 282 | errors.push_spanned( 283 | variant.variant, 284 | "additional flattened variants must have at least one unique non-flattened, non-default field", 285 | ); 286 | } else { 287 | variant_keys.insert(key); 288 | } 289 | } else { 290 | errors.push_spanned( 291 | variant.variant, 292 | "only enum variants with named fields can have `flatten`", 293 | ); 294 | } 295 | } 296 | } 297 | if let FieldStatus::Some(default) = &default { 298 | if variant_keys.contains(&BTreeSet::new()) { 299 | errors.push( 300 | default.span(), 301 | "`default` cannot be used when a flattened variant has no unique field", 302 | ); 303 | } 304 | } 305 | errors.check()?; 306 | Ok(Self { 307 | enum_, 308 | variants, 309 | default: default.into(), 310 | crate_: crate_.into(), 311 | attributes, 312 | and_thens, 313 | allow_unknown_fields: allow_unknown_fields.into(), 314 | }) 315 | }, 316 | ) 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /macros/types/struct.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use deluxe_core::{ 3 | parse_helpers::{self, FieldStatus}, 4 | ParseAttributes, ParseMetaItem, ParseMode, Result, 5 | }; 6 | use proc_macro2::{Span, TokenStream}; 7 | use quote::quote_spanned; 8 | use std::{borrow::Borrow, collections::HashSet}; 9 | use syn::{ 10 | parse::{ParseBuffer, ParseStream}, 11 | spanned::Spanned, 12 | }; 13 | 14 | pub struct StructTransparent { 15 | pub span: Span, 16 | pub value: bool, 17 | pub flatten_named: Option, 18 | pub flatten_unnamed: Option, 19 | pub append: Option, 20 | pub rest: Option, 21 | } 22 | 23 | impl quote::ToTokens for StructTransparent { 24 | #[inline] 25 | fn to_tokens(&self, tokens: &mut TokenStream) { 26 | syn::LitBool::new(self.value, self.span).to_tokens(tokens); 27 | } 28 | } 29 | 30 | impl ParseMetaItem for StructTransparent { 31 | fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { 32 | let lookahead = input.lookahead1(); 33 | if lookahead.peek(syn::LitBool) { 34 | let value = input.parse::()?; 35 | Ok(Self { 36 | span: value.span(), 37 | value: value.value(), 38 | flatten_named: None, 39 | flatten_unnamed: None, 40 | append: None, 41 | rest: None, 42 | }) 43 | } else if lookahead.peek(syn::token::Brace) { 44 | ::parse_delimited_meta_item( 45 | input, mode, 46 | ) 47 | } else { 48 | Err(lookahead.error()) 49 | } 50 | } 51 | fn parse_meta_item_inline<'s, S: Borrow>>( 52 | inputs: &[S], 53 | mode: ParseMode, 54 | ) -> Result { 55 | let errors = crate::Errors::new(); 56 | let span = mode.to_full_span(inputs); 57 | let mut flatten_named = FieldStatus::None; 58 | let mut flatten_unnamed = FieldStatus::None; 59 | let mut append = FieldStatus::None; 60 | let mut rest = FieldStatus::None; 61 | errors.push_result(parse_helpers::parse_struct(inputs, |input, path, span| { 62 | match path { 63 | "flatten_named" => { 64 | flatten_named.parse_named_item("flatten_named", input, span, &errors) 65 | } 66 | "flatten_unnamed" => { 67 | flatten_unnamed.parse_named_item("flatten_unnamed", input, span, &errors) 68 | } 69 | "append" => append.parse_named_item("append", input, span, &errors), 70 | "rest" => rest.parse_named_item("rest", input, span, &errors), 71 | _ => { 72 | errors.push_syn(parse_helpers::unknown_error( 73 | path, 74 | span, 75 | &["flatten_named", "flatten_unnamed", "append", "rest"], 76 | )); 77 | parse_helpers::skip_meta_item(input); 78 | } 79 | } 80 | Ok(()) 81 | })); 82 | errors.check()?; 83 | Ok(Self { 84 | span, 85 | value: true, 86 | flatten_named: flatten_named.into(), 87 | flatten_unnamed: flatten_unnamed.into(), 88 | append: append.into(), 89 | rest: rest.into(), 90 | }) 91 | } 92 | #[inline] 93 | fn parse_meta_item_flag(span: Span) -> Result { 94 | Ok(Self { 95 | span, 96 | value: true, 97 | flatten_named: None, 98 | flatten_unnamed: None, 99 | append: None, 100 | rest: None, 101 | }) 102 | } 103 | } 104 | 105 | pub struct Struct<'s> { 106 | pub struct_: &'s syn::DataStruct, 107 | pub fields: Vec>, 108 | pub default: Option, 109 | pub crate_: Option, 110 | pub transparent: Option, 111 | pub allow_unknown_fields: Option, 112 | pub attributes: Vec, 113 | pub and_thens: Vec, 114 | } 115 | 116 | impl<'s> Struct<'s> { 117 | #[inline] 118 | pub fn is_transparent(&self) -> bool { 119 | self.transparent.as_ref().map(|t| t.value).unwrap_or(false) 120 | && self 121 | .fields 122 | .iter() 123 | .filter(|f| f.is_parsable() && !f.is_flat()) 124 | .take(2) 125 | .count() 126 | == 1 127 | } 128 | #[inline] 129 | pub fn field_names() -> &'static [&'static str] { 130 | &[ 131 | "transparent", 132 | "default", 133 | "crate", 134 | "attributes", 135 | "and_then", 136 | "allow_unknown_fields", 137 | ] 138 | } 139 | #[inline] 140 | pub fn to_accepts_all_tokens(&self, crate_: &syn::Path) -> Option { 141 | if self.allow_unknown_fields.unwrap_or(false) { 142 | return Some(quote_mixed! { true }); 143 | } 144 | Field::to_accepts_all_tokens(&self.fields, crate_) 145 | } 146 | #[inline] 147 | pub fn to_field_names_tokens(&self, crate_: &syn::Path, priv_: &syn::Path) -> TokenStream { 148 | Field::to_field_names_tokens(&self.fields, crate_, priv_) 149 | } 150 | pub fn to_parsing_tokens( 151 | &self, 152 | orig: &syn::DeriveInput, 153 | crate_: &syn::Path, 154 | mode: TokenMode, 155 | inline_expr: &TokenStream, 156 | allowed_expr: &TokenStream, 157 | ) -> ItemDef { 158 | let priv_path: syn::Path = syn::parse_quote! { #crate_::____private }; 159 | let priv_ = &priv_path; 160 | let target = self.default.as_ref().map(|_| quote_mixed! { target }); 161 | let target = target 162 | .as_ref() 163 | .map(ParseTarget::Var) 164 | .unwrap_or_else(|| ParseTarget::Init(None)); 165 | let transparent = self.is_transparent(); 166 | let allow_unknown_fields = self.allow_unknown_fields.unwrap_or(false); 167 | let field_data = FieldData { 168 | mode, 169 | target, 170 | inline_expr, 171 | allowed_expr, 172 | transparent, 173 | variant: false, 174 | allow_unknown_fields, 175 | }; 176 | let orig_fields = match &orig.data { 177 | syn::Data::Struct(s) => &s.fields, 178 | _ => unreachable!(), 179 | }; 180 | let (mut pre, post) = 181 | Field::to_pre_post_tokens(&self.fields, orig_fields, crate_, &field_data); 182 | let post = if self.and_thens.is_empty() { 183 | post 184 | } else { 185 | let and_thens = &self.and_thens; 186 | quote_mixed! { 187 | let ret = { 188 | #post 189 | }; 190 | let errors = #crate_::Errors::new(); 191 | let ret = ret.ok(); 192 | #(let ret = ret.and_then(|v| { 193 | let f = #and_thens; 194 | errors.push_result(f(v)) 195 | });)* 196 | errors.check()?; 197 | #crate_::Result::Ok(ret.unwrap()) 198 | } 199 | }; 200 | let default_set = self.default.as_ref().map(|d| { 201 | let expr = d.to_expr(Some(&syn::parse_quote_spanned! { d.span() => Self }), priv_); 202 | quote_mixed! { 203 | let mut target: Self = (#expr); 204 | } 205 | }); 206 | if transparent { 207 | let (field, name) = self 208 | .fields 209 | .iter() 210 | .enumerate() 211 | .find_map(|(i, f)| { 212 | (f.is_parsable() && !f.is_flat()).then(|| { 213 | ( 214 | f, 215 | quote::format_ident!("field{i}", span = Span::mixed_site()), 216 | ) 217 | }) 218 | }) 219 | .unwrap(); 220 | let ty = &field.field.ty; 221 | let module = field 222 | .with 223 | .as_ref() 224 | .map(|m| quote_spanned! { m.span() => #m }); 225 | let parse_ty = module.clone().unwrap_or_else(|| { 226 | quote_spanned! { ty.span() => <#ty as #crate_::ParseMetaItem> } 227 | }); 228 | pre.extend(default_set); 229 | if matches!(orig_fields, syn::Fields::Unnamed(_)) { 230 | pre.extend(quote_mixed! { 231 | let index = 0usize; 232 | }); 233 | } 234 | let parse = quote_mixed! { 235 | #pre 236 | #name = #priv_::FieldStatus::Some(#parse_ty::parse_meta_item(input, _mode)?); 237 | #post 238 | }; 239 | let inline = Some(quote_mixed! { 240 | #pre 241 | #name = #priv_::FieldStatus::Some(#parse_ty::parse_meta_item_inline(inputs, _mode)?); 242 | #post 243 | }); 244 | let flag = Some(quote_mixed! { 245 | #pre 246 | #name = #priv_::FieldStatus::Some(#parse_ty::parse_meta_item_flag(span)?); 247 | #post 248 | }); 249 | let struct_ident = &orig.ident; 250 | let (impl_generics, type_generics, where_clause) = orig.generics.split_for_impl(); 251 | let flatten_named = self 252 | .transparent 253 | .as_ref() 254 | .and_then(|t| t.flatten_named) 255 | .unwrap_or(false); 256 | let flatten_unnamed = self 257 | .transparent 258 | .as_ref() 259 | .and_then(|t| t.flatten_unnamed) 260 | .unwrap_or(false); 261 | let rest = self 262 | .transparent 263 | .as_ref() 264 | .and_then(|t| t.rest) 265 | .unwrap_or(false); 266 | let append = self 267 | .transparent 268 | .as_ref() 269 | .and_then(|t| t.append) 270 | .unwrap_or(false); 271 | 272 | let mut extra_traits = Vec::new(); 273 | if flatten_named { 274 | let flat_ty = module.clone().unwrap_or_else(|| { 275 | quote_spanned! { ty.span() => <#ty as #crate_::ParseMetaFlatNamed> } 276 | }); 277 | extra_traits.push(quote_mixed! { 278 | impl #impl_generics #crate_::ParseMetaFlatNamed for #struct_ident #type_generics #where_clause { 279 | const ACCEPTS_ALL: #priv_::bool = #flat_ty::ACCEPTS_ALL; 280 | #[inline] 281 | fn field_names() -> &'static [&'static #priv_::str] { 282 | #flat_ty::field_names() 283 | } 284 | #[inline] 285 | fn parse_meta_flat_named<'s, S: #priv_::Borrow<#priv_::ParseBuffer<'s>>>( 286 | inputs: &[S], 287 | mode: #crate_::ParseMode, 288 | prefix: &#priv_::str, 289 | validate: #priv_::bool, 290 | ) -> #crate_::Result { 291 | #pre 292 | #name = #priv_::FieldStatus::Some(#flat_ty::parse_meta_flat_named(inputs, mode, prefix, validate)?); 293 | #post 294 | } 295 | } 296 | }); 297 | } 298 | if flatten_unnamed { 299 | let flat_ty = module.clone().unwrap_or_else(|| { 300 | quote_spanned! { ty.span() => <#ty as #crate_::ParseMetaFlatUnnamed> } 301 | }); 302 | extra_traits.push(quote_mixed! { 303 | impl #impl_generics #crate_::ParseMetaFlatUnnamed for #struct_ident #type_generics #where_clause { 304 | #[inline] 305 | fn field_count() -> #priv_::Option<#priv_::usize> { 306 | #flat_ty::field_count() 307 | } 308 | #[inline] 309 | fn parse_meta_flat_unnamed<'s, S: #priv_::Borrow<#priv_::ParseBuffer<'s>>>( 310 | inputs: &[S], 311 | mode: #crate_::ParseMode, 312 | index: #priv_::usize, 313 | ) -> #crate_::Result { 314 | #pre 315 | #name = #priv_::FieldStatus::Some(#flat_ty::parse_meta_flat_unnamed(inputs, mode, index)?); 316 | #post 317 | } 318 | } 319 | }); 320 | } 321 | if rest { 322 | let rest_ty = module.clone().unwrap_or_else(|| { 323 | quote_spanned! { ty.span() => <#ty as #crate_::ParseMetaRest> } 324 | }); 325 | extra_traits.push(quote_mixed! { 326 | impl #impl_generics #crate_::ParseMetaRest for #struct_ident #type_generics #where_clause { 327 | #[inline] 328 | fn parse_meta_rest<'s, S: #priv_::Borrow<#priv_::ParseBuffer<'s>>>( 329 | inputs: &[S], 330 | exclude: &[&#priv_::str], 331 | ) -> #crate_::Result { 332 | #pre 333 | #name = #priv_::FieldStatus::Some(#rest_ty::parse_meta_rest(inputs, exclude)?); 334 | #post 335 | } 336 | } 337 | }); 338 | } 339 | if append { 340 | let append_ty = module.unwrap_or_else(|| { 341 | quote_spanned! { ty.span() => <#ty as #crate_::ParseMetaAppend> } 342 | }); 343 | extra_traits.push(quote_mixed! { 344 | impl #impl_generics #crate_::ParseMetaAppend for #struct_ident #type_generics #where_clause { 345 | #[inline] 346 | fn parse_meta_append<'s, S, I, P>(inputs: &[S], paths: I) -> #crate_::Result 347 | where 348 | S: #priv_::Borrow<#priv_::ParseBuffer<'s>>, 349 | I: #priv_::IntoIterator, 350 | I::IntoIter: #priv_::Clone, 351 | P: #priv_::AsRef<#priv_::str> 352 | { 353 | #pre 354 | #name = #priv_::FieldStatus::Some(#append_ty::parse_meta_append(inputs, paths)?); 355 | #post 356 | } 357 | } 358 | }); 359 | } 360 | let extra_traits = (!extra_traits.is_empty()).then(|| { 361 | quote_mixed! { 362 | #(#extra_traits)* 363 | } 364 | }); 365 | ItemDef { 366 | parse, 367 | inline, 368 | flag, 369 | extra_traits, 370 | } 371 | } else { 372 | let mut def = Field::to_parsing_tokens( 373 | &self.fields, 374 | orig_fields, 375 | crate_, 376 | (&pre, &post), 377 | field_data, 378 | ); 379 | def.inline = def.inline.map(|inline| { 380 | quote_mixed! { 381 | #default_set 382 | #inline 383 | } 384 | }); 385 | def 386 | } 387 | } 388 | } 389 | 390 | deluxe_core::define_with_collection!(mod mod_path_vec, deluxe_core::with::mod_path, Vec); 391 | 392 | impl<'s> ParseAttributes<'s, syn::DeriveInput> for Struct<'s> { 393 | #[inline] 394 | fn path_matches(path: &syn::Path) -> bool { 395 | path.is_ident("deluxe") 396 | } 397 | fn parse_attributes(i: &'s syn::DeriveInput) -> Result { 398 | parse_helpers::parse_struct_attr_tokens( 399 | parse_helpers::ref_tokens::(i), 400 | |inputs, _| { 401 | let struct_ = match &i.data { 402 | syn::Data::Struct(s) => s, 403 | _ => return Err(syn::Error::new_spanned(i, "wrong DeriveInput type")), 404 | }; 405 | let errors = crate::Errors::new(); 406 | let mut transparent = FieldStatus::None; 407 | let mut allow_unknown_fields = FieldStatus::None; 408 | let mut default = FieldStatus::None; 409 | let mut crate_ = FieldStatus::None; 410 | let mut and_thens = Vec::new(); 411 | let mut attributes = Vec::new(); 412 | let fields = struct_ 413 | .fields 414 | .iter() 415 | .filter_map(|f| errors.push_result(Field::parse_attributes(f))) 416 | .collect::>(); 417 | errors.push_result(parse_helpers::parse_struct(inputs, |input, path, span| { 418 | match path { 419 | "transparent" => { 420 | transparent.parse_named_item_with( 421 | "transparent", 422 | input, 423 | span, 424 | &errors, 425 | |input, _, span| { 426 | let mut iter = fields.iter().filter(|f| f.is_parsable()); 427 | if let Some(first) = iter.next() { 428 | if first.flatten.as_ref().map(|f| f.value).unwrap_or(false) 429 | { 430 | return Err(syn::Error::new( 431 | span, 432 | "`transparent` struct field cannot be `flat`", 433 | )); 434 | } else if first.append.map(|v| *v).unwrap_or(false) { 435 | return Err(syn::Error::new( 436 | span, 437 | "`transparent` struct field cannot be `append`", 438 | )); 439 | } else if iter.next().is_none() { 440 | return <_>::parse_meta_item_named(input, path, span); 441 | } 442 | } 443 | Err(syn::Error::new( 444 | span, 445 | "`transparent` struct must have only one parseable field", 446 | )) 447 | }, 448 | ); 449 | } 450 | "allow_unknown_fields" => { 451 | if matches!(struct_.fields, syn::Fields::Unnamed(_)) { 452 | errors.push( 453 | span, 454 | "`allow_unknown_fields` not allowed on tuple struct", 455 | ); 456 | parse_helpers::skip_meta_item(input); 457 | } else { 458 | allow_unknown_fields.parse_named_item( 459 | "allow_unknown_fields", 460 | input, 461 | span, 462 | &errors, 463 | ); 464 | } 465 | } 466 | "default" => { 467 | if matches!(struct_.fields, syn::Fields::Unit) { 468 | errors.push(span, "`default` not allowed on unit struct"); 469 | parse_helpers::skip_meta_item(input); 470 | } else { 471 | default.parse_named_item("default", input, span, &errors); 472 | } 473 | } 474 | "crate" => crate_.parse_named_item("crate", input, span, &errors), 475 | "and_then" => { 476 | match errors.push_result(<_>::parse_meta_item_named(input, path, span)) 477 | { 478 | Some(e) => and_thens.push(e), 479 | None => parse_helpers::skip_meta_item(input), 480 | } 481 | } 482 | "attributes" => { 483 | match errors 484 | .push_result(mod_path_vec::parse_meta_item_named(input, path, span)) 485 | { 486 | Some(attrs) => attributes.extend(attrs), 487 | None => parse_helpers::skip_meta_item(input), 488 | } 489 | } 490 | _ => { 491 | parse_helpers::check_unknown_attribute( 492 | path, 493 | span, 494 | Self::field_names(), 495 | &errors, 496 | ); 497 | parse_helpers::skip_meta_item(input); 498 | } 499 | } 500 | Ok(()) 501 | })); 502 | let fields = { 503 | let mut fields = fields; 504 | if default.is_none() { 505 | for field in &mut fields { 506 | if let Some(span) = 507 | field.skip.and_then(|skip| (*skip).then_some(skip.span())) 508 | { 509 | if field.default.is_none() { 510 | field.default = Some(FieldDefault::Default(span)); 511 | } 512 | } 513 | } 514 | } 515 | fields 516 | }; 517 | let mut all_idents = HashSet::new(); 518 | let mut container = None; 519 | for field in &fields { 520 | for ident in &field.idents { 521 | if all_idents.contains(ident) { 522 | errors.push_spanned( 523 | ident, 524 | format_args!("duplicate field name for `{ident}`"), 525 | ); 526 | } else { 527 | all_idents.insert(ident.clone()); 528 | } 529 | } 530 | if let Some(c) = field.container.as_ref() { 531 | if container.is_some() { 532 | errors.push(c.span(), "Duplicate `container` field") 533 | } else { 534 | container = Some(c); 535 | } 536 | } 537 | } 538 | if matches!(struct_.fields, syn::Fields::Unnamed(_)) { 539 | let mut has_default_gap = false; 540 | for field in fields.iter().rev() { 541 | if field.is_parsable() && !field.is_flat() { 542 | if let Some(default) = &field.default { 543 | if has_default_gap { 544 | errors.push( 545 | default.span(), 546 | "`default` fields can only be at the end of a tuple struct", 547 | ); 548 | } 549 | } else { 550 | has_default_gap = true; 551 | } 552 | } 553 | } 554 | } 555 | errors.check()?; 556 | Ok(Self { 557 | struct_, 558 | fields, 559 | default: default.into(), 560 | crate_: crate_.into(), 561 | transparent: transparent.into(), 562 | allow_unknown_fields: allow_unknown_fields.into(), 563 | attributes, 564 | and_thens, 565 | }) 566 | }, 567 | ) 568 | } 569 | } 570 | -------------------------------------------------------------------------------- /macros/util.rs: -------------------------------------------------------------------------------- 1 | use syn::parse::{Parse, Parser}; 2 | 3 | use deluxe_core::Errors; 4 | 5 | #[inline] 6 | pub fn parse(input: proc_macro::TokenStream, errors: &Errors) -> Option { 7 | errors.push_result(::parse.parse(input)) 8 | } 9 | 10 | fn crate_path(errors: Option<&Errors>) -> Option { 11 | use proc_macro_crate::FoundCrate; 12 | const CRATE_NAME: &str = "deluxe"; 13 | 14 | let crate_name = match proc_macro_crate::crate_name(CRATE_NAME) { 15 | Ok(FoundCrate::Name(name)) => name, 16 | Ok(FoundCrate::Itself) => CRATE_NAME.into(), 17 | Err(e) => { 18 | if let Some(errors) = errors { 19 | errors.push(proc_macro2::Span::call_site(), e.to_string()); 20 | } 21 | return None; 22 | } 23 | }; 24 | 25 | let ident = syn::Ident::new(&crate_name, proc_macro2::Span::call_site()); 26 | Some(syn::parse_quote! { ::#ident }) 27 | } 28 | 29 | #[inline] 30 | pub fn get_crate_path(path: Option>, errors: &Errors) -> Option { 31 | match path { 32 | Some(Some(path)) => Some(path), 33 | Some(None) => crate_path(Some(errors)), 34 | None => crate_path(None), 35 | } 36 | } 37 | 38 | macro_rules! quote_mixed { 39 | ($($tt:tt)*) => { 40 | quote::quote_spanned! { Span::mixed_site() => $($tt)* } 41 | }; 42 | } 43 | 44 | macro_rules! parse_quote_mixed { 45 | ($($tt:tt)*) => { 46 | syn::parse_quote_spanned! { Span::mixed_site() => $($tt)* } 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Deluxe 2 | //! 3 | //! A procedural macro attribute parser. 4 | //! 5 | //! ### Abstract 6 | //! 7 | //! This crate offers attribute parsing closer to the design of attributes in C#. It has an 8 | //! interface similar to [serde](https://serde.rs). Attributes are written as plain Rust structs or 9 | //! enums, and then parsers for them are generated automatically. They can contain arbitrary 10 | //! expressions and can inherit from other attributes using a flattening mechanism. 11 | //! 12 | //! The parsers in this crate directly parse token streams using [`syn`]. As a result, most 13 | //! built-in Rust types and `syn` types can be used directly as fields. 14 | //! 15 | //! ### Usage 16 | //! 17 | //! Functionality in this crate is centered around three traits, and their respective derive macros: 18 | //! - **[`ExtractAttributes`](macro@ExtractAttributes)** 19 | //! 20 | //! Extracts attributes from an object containing a list of [`syn::Attribute`], and parses them 21 | //! into a Rust type. Should be implemented for top-level structures that will be parsed directly 22 | //! out of a set of matching attributes. 23 | //! - **[`ParseAttributes`](macro@ParseAttributes)** 24 | //! 25 | //! Parses a Rust type from any object containing a list of [`syn::Attribute`]. Should be used if 26 | //! the set of matching attributes can potentially be shared between this type and other types. 27 | //! - **[`ParseMetaItem`](macro@ParseMetaItem)** 28 | //! 29 | //! Parses a Rust type from a [`ParseStream`](syn::parse::ParseStream). Should be implemented for 30 | //! any types that can be nested inside an attribute. 31 | //! 32 | //! Basic usage of this crate in derive macros requires simply deriving one (or a few) of these 33 | //! traits, and then calling [`extract_attributes`] or [`parse_attributes`]. For more advanced 34 | //! functionality, several `#[deluxe(...)]` attributes are supported on structs, enums, variants 35 | //! and fields. See the examples below, and the documentation for each derive macro for a complete 36 | //! description of the supported attributes. 37 | //! 38 | //! A list of field types supported by default can be seen in the list of provided [`ParseMetaItem` 39 | //! implementations](trait@ParseMetaItem#foreign-impls). For more complex usage, manual 40 | //! implementations of these traits can be provided. See the documentation on individual traits in 41 | //! [`deluxe_core`] for more details on how to manually implement your own parsers. 42 | //! 43 | //! ### Related Crates 44 | //! 45 | //! Deluxe takes inspiration from the [darling](https://docs.rs/darling) crate, but offers a few 46 | //! enhancements over it. Darling is built around pre-parsed [`syn::Meta`] objects, and therefore 47 | //! is restricted to the [meta 48 | //! syntax](https://doc.rust-lang.org/stable/reference/attributes.html#meta-item-attribute-syntax). 49 | //! Deluxe parses its types directly from [`TokenStream`](proc_macro2::TokenStream) objects in the 50 | //! attributes and so is able to use any syntax that parses as a valid token tree. Deluxe also does 51 | //! not provide extra traits for parsing special `syn` objects like 52 | //! [`DeriveInput`](syn::DeriveInput) and [`Field`](syn::Field). Instead, Deluxe uses a generic 53 | //! trait to parse from any type containing a [Vec]<[syn::Attribute]>. 54 | //! 55 | //! ### Examples 56 | //! 57 | //! #### Basic Derive Macro 58 | //! 59 | //! To create a derive macro that can add some simple metadata to a Rust type from an attribute, 60 | //! start by defining a struct that derives [`ExtractAttributes`](macro@ExtractAttributes). Then, 61 | //! call [`extract_attributes`] in your derive macro to create an instance of the struct: 62 | //! 63 | //! ``` 64 | //! #[derive(deluxe::ExtractAttributes)] 65 | //! #[deluxe(attributes(my_desc))] 66 | //! struct MyDescription { 67 | //! name: String, 68 | //! version: String, 69 | //! } 70 | //! 71 | //! fn my_derive(item: proc_macro2::TokenStream) -> deluxe::Result { 72 | //! let mut input = syn::parse2::(item)?; 73 | //! 74 | //! // Extract the attributes! 75 | //! let MyDescription { name, version } = deluxe::extract_attributes(&mut input)?; 76 | //! 77 | //! // Now get some info to generate an associated function... 78 | //! let ident = &input.ident; 79 | //! let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); 80 | //! 81 | //! Ok(quote::quote! { 82 | //! impl #impl_generics #ident #type_generics #where_clause { 83 | //! fn my_desc() -> &'static str { 84 | //! concat!("Name: ", #name, ", Version: ", #version) 85 | //! } 86 | //! } 87 | //! }) 88 | //! } 89 | //! 90 | //! # let tokens = my_derive(quote::quote! { 91 | //! # #[my_desc(name = "hello world", version = "0.2")] 92 | //! # struct Hello; 93 | //! # }).unwrap(); 94 | //! # let i: syn::ItemImpl = syn::parse_quote! { #tokens }; 95 | //! # assert_eq!(i, syn::parse_quote! { 96 | //! # impl Hello { 97 | //! # fn my_desc() -> &'static str { 98 | //! # concat!("Name: ", "hello world", ", Version: ", "0.2") 99 | //! # } 100 | //! # } 101 | //! # }); 102 | //! ``` 103 | //! 104 | //! Then, try adding the attribute in some code that uses your macro: 105 | //! 106 | //! ```ignore 107 | //! // In your macros crate 108 | //! 109 | //! #[proc_macro_derive(MyDescription, attributes(my_desc))] 110 | //! pub fn derive_my_description(item: proc_macro::TokenStream) -> proc_macro::TokenStream { 111 | //! my_derive(item.into()).unwrap().into() 112 | //! } 113 | //! ``` 114 | //! 115 | //! ```ignore 116 | //! // In your normal code 117 | //! 118 | //! #[derive(MyDescription, Default)] 119 | //! #[my_desc(name = "hello world", version = "0.2")] 120 | //! struct Hello { 121 | //! a: i32, 122 | //! b: String 123 | //! } 124 | //! 125 | //! let hello: Hello = Default::default(); 126 | //! assert_eq!(hello.my_desc(), "Name: hello world, Version: 0.2"); 127 | //! ``` 128 | //! 129 | //! #### Basic Attribute Macro 130 | //! 131 | //! The `parse` and `parse2` functions included in this crate can also be used as simple helpers 132 | //! for attribute macros: 133 | //! 134 | //! ``` 135 | //! #[derive(deluxe::ParseMetaItem)] 136 | //! struct MyDescription { 137 | //! name: String, 138 | //! version: String, 139 | //! } 140 | //! 141 | //! fn my_desc_attr( 142 | //! attr: proc_macro2::TokenStream, 143 | //! item: proc_macro2::TokenStream, 144 | //! ) -> deluxe::Result { 145 | //! let MyDescription { name, version } = deluxe::parse2::(attr)?; 146 | //! 147 | //! Ok(quote::quote! { 148 | //! fn my_desc() -> &'static str { 149 | //! concat!("Name: ", #name, ", Version: ", #version) 150 | //! } 151 | //! #item 152 | //! }) 153 | //! } 154 | //! 155 | //! # let tokens = my_desc_attr( 156 | //! # quote::quote!(name = "hello world", version = "0.2"), 157 | //! # quote::quote!(), 158 | //! # ).unwrap(); 159 | //! # let f: syn::ItemFn = syn::parse_quote! { #tokens }; 160 | //! # assert_eq!(f, syn::parse_quote! { 161 | //! # fn my_desc() -> &'static str { 162 | //! # concat!("Name: ", "hello world", ", Version: ", "0.2") 163 | //! # } 164 | //! # }); 165 | //! ``` 166 | //! 167 | //! ```ignore 168 | //! // In your macros crate 169 | //! 170 | //! #[proc_macro_attribute] 171 | //! pub fn my_desc( 172 | //! attr: proc_macro::TokenStream, 173 | //! item: proc_macro::TokenStream, 174 | //! ) -> proc_macro::TokenStream { 175 | //! my_desc_attr(attr.into(), item.into()).unwrap().into() 176 | //! } 177 | //! ``` 178 | //! 179 | //! ```ignore 180 | //! // In your normal code 181 | //! 182 | //! #[my_desc(name = "hello world", version = "0.2")] 183 | //! fn nothing() {} 184 | //! 185 | //! assert_eq!(my_desc(), "Name: hello world, Version: 0.2"); 186 | //! ``` 187 | //! 188 | //! #### Field Attributes 189 | //! 190 | //! The attributes [`alias`](macro@ParseMetaItem#deluxealias--ident-1), 191 | //! [`default`](macro@ParseMetaItem#deluxedefault-1), 192 | //! [`rename`](macro@ParseMetaItem#deluxerename--ident-1), and 193 | //! [`skip`](macro@ParseMetaItem#deluxeskip-1) are supported, and behave the same as in Serde. The 194 | //! [`append`](macro@ParseMetaItem#deluxeappend) attribute can be used on [`Vec`] fields to 195 | //! aggregate all duplicates of a key. The [`rest`](macro@ParseMetaItem#deluxerest) attribute can 196 | //! be used to do custom processing on any unknown keys. 197 | //! 198 | //! ``` 199 | //! #[derive(deluxe::ExtractAttributes)] 200 | //! #[deluxe(attributes(my_object))] 201 | //! struct MyObject { 202 | //! // Can be specified with key `id` or `object_id` 203 | //! #[deluxe(alias = object_id)] 204 | //! id: u64, 205 | //! 206 | //! // Field is optional, defaults to `Default::default` if not present 207 | //! #[deluxe(default)] 208 | //! count: u64, 209 | //! 210 | //! // Defaults to "Empty" if not present 211 | //! #[deluxe(default = String::from("Empty"))] 212 | //! contents: String, 213 | //! 214 | //! // Can be specified only with key `name` 215 | //! #[deluxe(rename = name)] 216 | //! s: String, 217 | //! 218 | //! // Skipped during parsing entirely 219 | //! #[deluxe(skip)] 220 | //! internal_flag: bool, 221 | //! 222 | //! // Appends any extra fields with the key `expr` to the Vec 223 | //! #[deluxe(append, rename = expr)] 224 | //! exprs: Vec, 225 | //! 226 | //! // Adds any unknown keys to the hash map 227 | //! #[deluxe(rest)] 228 | //! rest: std::collections::HashMap, 229 | //! } 230 | //! ``` 231 | //! 232 | //! ```ignore 233 | //! // Omitted fields will be set to defaults 234 | //! #[derive(MyObject)] 235 | //! #[my_object(id = 1, name = "First", expr = 1 + 2, count = 3)] 236 | //! struct FirstObject; 237 | //! 238 | //! // `expr` can be specified multiple times because of the `append` attribute 239 | //! #[derive(MyObject)] 240 | //! #[my_object(object_id = 2, name = "Second", expr = 1 + 2, expr = 3 + 4)] 241 | //! struct SecondObject; 242 | //! 243 | //! // `unknown` and `extra` will be stored in the `rest` hashmap 244 | //! #[derive(MyObject)] 245 | //! #[my_object(id = 3, name = "Third", unknown = 1 + 2, extra = 3 + 4)] 246 | //! struct ThirdObject; 247 | //! ``` 248 | //! 249 | //! #### Inheritance 250 | //! 251 | //! The [`flatten`](macro@ParseMetaItem#deluxeflatten-1) attribute can be used to parse keys from 252 | //! one structure inside another: 253 | //! 254 | //! ``` 255 | //! #[derive(deluxe::ParseMetaItem)] 256 | //! struct A { 257 | //! id: u64, 258 | //! } 259 | //! 260 | //! #[derive(deluxe::ExtractAttributes)] 261 | //! #[deluxe(attributes(b))] 262 | //! struct B { 263 | //! #[deluxe(flatten)] 264 | //! a: A, 265 | //! name: String, 266 | //! } 267 | //! ``` 268 | //! 269 | //! Then, fields from both `A` and `B` can be used when deriving `B`: 270 | //! 271 | //! ```ignore 272 | //! #[derive(B)] 273 | //! #[b(id = 123, name = "object")] 274 | //! struct Object; 275 | //! ``` 276 | //! 277 | //! #### Attributes in Nested Code 278 | //! 279 | //! Extra attributes can be taken from within the code block attached to a macro. When used in an 280 | //! attribute macro, the attributes should be consumed so as not to produce an "unknown attribute" 281 | //! error when outputting tokens. 282 | //! 283 | //! ``` 284 | //! #[derive(deluxe::ParseMetaItem, deluxe::ExtractAttributes)] 285 | //! struct MyDescription { 286 | //! name: String, 287 | //! version: String, 288 | //! } 289 | //! 290 | //! #[derive(deluxe::ExtractAttributes)] 291 | //! #[deluxe(attributes(author))] 292 | //! struct Authors(#[deluxe(flatten)] Vec); 293 | //! 294 | //! fn my_derive(item: proc_macro2::TokenStream) -> deluxe::Result { 295 | //! let mut input = syn::parse2::(item)?; 296 | //! let MyDescription { name, version } = deluxe::extract_attributes(&mut input)?; 297 | //! let mut authors = Vec::new(); 298 | //! if let syn::Data::Struct(s) = &mut input.data { 299 | //! // Look through all fields in the struct for `author` attributes 300 | //! for field in s.fields.iter_mut() { 301 | //! let Authors(a) = deluxe::extract_attributes(field)?; 302 | //! authors.extend(a); 303 | //! } 304 | //! } 305 | //! 306 | //! let ident = &input.ident; 307 | //! let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); 308 | //! 309 | //! Ok(quote::quote! { 310 | //! impl #impl_generics #ident #type_generics #where_clause { 311 | //! fn my_desc() -> &'static str { 312 | //! concat!("Name: ", #name, ", Version: ", #version #(, ", Author: ", #authors)*) 313 | //! } 314 | //! } 315 | //! }) 316 | //! } 317 | //! 318 | //! # let tokens = my_derive(quote::quote! { 319 | //! # #[my_desc(name = "hello world", version = "0.2")] 320 | //! # struct Hello(#[author("Alice")] String); 321 | //! # }).unwrap(); 322 | //! # let i: syn::ItemImpl = syn::parse_quote! { #tokens }; 323 | //! # assert_eq!(i, syn::parse_quote! { 324 | //! # impl Hello { 325 | //! # fn my_desc() -> &'static str { 326 | //! # concat!("Name: ", "hello world", ", Version: ", "0.2", ", Author: ", "Alice") 327 | //! # } 328 | //! # } 329 | //! # }); 330 | //! 331 | //! fn my_desc_mod( 332 | //! attr: proc_macro2::TokenStream, 333 | //! item: proc_macro2::TokenStream, 334 | //! ) -> deluxe::Result { 335 | //! let MyDescription { name, version } = deluxe::parse2::(attr)?; 336 | //! let mut authors = Vec::new(); 337 | //! let mut module = syn::parse2::(item)?; 338 | //! 339 | //! let (_, items) = module.content.as_mut().unwrap(); 340 | //! 341 | //! // Look through all items in the module for `author` attributes 342 | //! for i in items.iter_mut() { 343 | //! // Extract the attributes to remove them from the final output 344 | //! let Authors(a) = deluxe::extract_attributes(i)?; 345 | //! authors.extend(a); 346 | //! } 347 | //! 348 | //! // Place a new function inside the module 349 | //! items.push(syn::parse_quote! { 350 | //! fn my_desc() -> &'static str { 351 | //! concat!("Name: ", #name, ", Version: ", #version #(, ", Author: ", #authors)*) 352 | //! } 353 | //! }); 354 | //! 355 | //! Ok(quote::quote! { #module }) 356 | //! } 357 | //! 358 | //! # let tokens = my_desc_mod( 359 | //! # quote::quote!(name = "hello world", version = "0.2"), 360 | //! # quote::quote!(mod abc { 361 | //! # #[author("Alice", "Bob")] 362 | //! # fn func1() {} 363 | //! # #[author("Carol")] 364 | //! # #[author("Dave")] 365 | //! # fn func2() {} 366 | //! # }), 367 | //! # ).unwrap(); 368 | //! # let m: syn::ItemMod = syn::parse_quote! { #tokens }; 369 | //! # assert_eq!(m, syn::parse_quote! { 370 | //! # mod abc { 371 | //! # fn func1() {} 372 | //! # fn func2() {} 373 | //! # fn my_desc() -> &'static str { 374 | //! # concat!( 375 | //! # "Name: ", "hello world", ", Version: ", "0.2", 376 | //! # ", Author: ", "Alice", ", Author: ", "Bob", 377 | //! # ", Author: ", "Carol", ", Author: ", "Dave" 378 | //! # ) 379 | //! # } 380 | //! # } 381 | //! # }); 382 | //! ``` 383 | //! 384 | //! ```ignore 385 | //! // In your normal code 386 | //! 387 | //! #[derive(MyDescription, Default)] 388 | //! #[my_desc(name = "hello world", version = "0.2")] 389 | //! struct Hello { 390 | //! #[author("Alice")] 391 | //! a: i32, 392 | //! #[author("Bob")] 393 | //! b: String 394 | //! } 395 | //! 396 | //! let hello: Hello = Default::default(); 397 | //! assert_eq!(hello.my_desc(), "Name: hello world, Version: 0.2, Author: Alice, Author: Bob"); 398 | //! 399 | //! #[my_desc_mod(name = "hello world", version = "0.2")] 400 | //! mod abc { 401 | //! #[author("Alice", "Bob")] 402 | //! fn func1() {} 403 | //! 404 | //! #[author("Carol")] 405 | //! #[author("Dave")] 406 | //! fn func2() {} 407 | //! } 408 | //! 409 | //! assert_eq!( 410 | //! abc::my_desc(), 411 | //! "Name: hello world, Version: 0.2, Author: Alice, Author: Bob, Author: Carol, Author: Dave" 412 | //! ); 413 | //! ``` 414 | //! 415 | //! #### Tuple Structs, Tuples and Vecs 416 | //! 417 | //! Deluxe also supports parsing into data structures with unnamed fields. 418 | //! 419 | //! ``` 420 | //! #[derive(deluxe::ExtractAttributes)] 421 | //! #[deluxe(attributes(my_tuple))] 422 | //! struct MyTuple(u64, String); 423 | //! 424 | //! #[derive(deluxe::ExtractAttributes)] 425 | //! #[deluxe(attributes(my_idents))] 426 | //! struct MyIdents { 427 | //! id: u64, 428 | //! names: (String, String), 429 | //! idents: Vec 430 | //! } 431 | //! ``` 432 | //! 433 | //! The standard attribute syntax with parenthesis can be used when specifying a [`Vec`] type. The 434 | //! alternative syntax `key = [...]` can also be used to have an appearance similar to an array 435 | //! literal. 436 | //! 437 | //! ```ignore 438 | //! #[derive(MyTuple)] 439 | //! #[my_tuple(123, "object")] 440 | //! struct Object; 441 | //! 442 | //! #[derive(MyIdents)] 443 | //! #[my_idents(id = 7, names("hello", "world"), idents(a, b, c))] 444 | //! struct ABC; 445 | //! 446 | //! // `idents` contains same values as above 447 | //! #[derive(MyIdents)] 448 | //! #[my_idents(id = 7, names("hello", "world"), idents = [a, b, c])] 449 | //! struct ABC2; 450 | //! ``` 451 | //! 452 | //! #### C#-styled Attributes 453 | //! 454 | //! Attributes in C# can support positional arguments first with the named 455 | //! arguments afterwards. This style can be emulated by using a tuple struct with a 456 | //! normal struct flattened at the end. Placing 457 | //! [`#[deluxe(default)]`](macro@ParseMetaItem#deluxedefault) on the struct behaves the same as 458 | //! Serde, by filling in all fields with values from [`Default`], allowing every named argument to 459 | //! be optional. 460 | //! 461 | //! ``` 462 | //! #[derive(deluxe::ParseMetaItem, Default)] 463 | //! #[deluxe(default)] 464 | //! struct Flags { 465 | //! native: bool, 466 | //! } 467 | //! 468 | //! #[derive(deluxe::ExtractAttributes)] 469 | //! #[deluxe(attributes(a))] 470 | //! struct A(u64, String, #[deluxe(flatten)] Flags); 471 | //! ``` 472 | //! 473 | //! ```ignore 474 | //! #[derive(A)] 475 | //! #[a(123, "object")] 476 | //! struct Object; 477 | //! 478 | //! #[derive(A)] 479 | //! #[a(123, "native-object", native = true)] 480 | //! struct NativeObject; 481 | //! ``` 482 | //! 483 | //! #### Enums 484 | //! 485 | //! Enums are supported by using the variant name as a single key, in snake-case. Variants can be 486 | //! renamed, aliased and skipped in the same way as fields. 487 | //! 488 | //! ``` 489 | //! #[derive(deluxe::ExtractAttributes)] 490 | //! #[deluxe(attributes(my_enum))] 491 | //! enum MyEnum { 492 | //! A, 493 | //! B, 494 | //! C, 495 | //! #[deluxe(alias = d)] 496 | //! AnotherOne, 497 | //! #[deluxe(rename = e)] 498 | //! AnotherTwo, 499 | //! #[deluxe(skip)] 500 | //! SkipMe 501 | //! } 502 | //! ``` 503 | //! 504 | //! ```ignore 505 | //! #[derive(MyEnum)] 506 | //! #[my_enum(b)] 507 | //! struct ObjectB; 508 | //! 509 | //! #[derive(MyEnum)] 510 | //! #[my_enum(another_one)] 511 | //! struct ObjectD; 512 | //! ``` 513 | //! 514 | //! #### Complex Enums 515 | //! 516 | //! Enums with struct and tuple variants are also supported. The data inside is used as arguments 517 | //! to the attribute. All field attributes from structs are also supported inside variants. 518 | //! 519 | //! Additionally, enum variants with named fields can be flattened. The behavior of a flattened 520 | //! variant is similar to Serde's `untagged` mode. In a flattened variant, the name of the variant 521 | //! will be ignored. Instead, Deluxe will attempt to use the unique keys in each variant to 522 | //! determine if that variant was specified. A compile error will be thrown if it is not possible 523 | //! to determine a unique, unambiguous key between two variants. 524 | //! 525 | //! ``` 526 | //! #[derive(deluxe::ExtractAttributes)] 527 | //! #[deluxe(attributes(my_enum))] 528 | //! enum MyEnum { 529 | //! A, 530 | //! B(u64, String), 531 | //! C { id: u64, name: String }, 532 | //! #[deluxe(flatten)] 533 | //! D { d: u64, name: String }, 534 | //! } 535 | //! ``` 536 | //! 537 | //! ```ignore 538 | //! #[derive(MyEnum)] 539 | //! #[my_enum(a)] 540 | //! struct ObjectA; 541 | //! 542 | //! #[derive(MyEnum)] 543 | //! #[my_enum(b(1, "hello"))] 544 | //! struct ObjectB; 545 | //! 546 | //! #[derive(MyEnum)] 547 | //! #[my_enum(c(id = 2, name = "world"))] 548 | //! struct ObjectC; 549 | //! 550 | //! // No inner parenthesis needed here due to flattening 551 | //! #[derive(MyEnum)] 552 | //! #[my_enum(d = 3, name = "moon")] 553 | //! struct ObjectD; 554 | //! ``` 555 | //! 556 | //! #### Storing Containers 557 | //! 558 | //! During parsing, Deluxe can store references to the container type holding the attributes for 559 | //! easier access. Container fields are skipped during attribute parsing. 560 | //! 561 | //! ``` 562 | //! #[derive(deluxe::ParseAttributes)] 563 | //! #[deluxe(attributes(my_object))] 564 | //! struct MyObject<'t> { 565 | //! id: u64, 566 | //! // Fill `container` in using the parsed type. Note this restricts the 567 | //! // derived `ParseAttributes` impl so it can only be used on `DeriveInput`. 568 | //! #[deluxe(container)] 569 | //! container: &'t syn::DeriveInput, 570 | //! } 571 | //! 572 | //! fn my_object(item: proc_macro2::TokenStream) -> deluxe::Result { 573 | //! let input = syn::parse2::(item)?; 574 | //! 575 | //! // `obj.container` now holds a reference to `input` 576 | //! let obj: MyObject = deluxe::parse_attributes(&input)?; 577 | //! 578 | //! Ok(quote::quote! { /* ... generate some code here ... */ }) 579 | //! } 580 | //! ``` 581 | //! 582 | //! To support both extracting and parsing, a container field can also be a value type. In that 583 | //! case, the container will be cloned into the structure. 584 | 585 | #![deny(missing_docs)] 586 | #![deny(unsafe_code)] 587 | 588 | #[doc(hidden)] 589 | pub mod ____private { 590 | pub use deluxe_core::parse_helpers::{self, FieldStatus, SmallString}; 591 | pub use once_cell::sync::OnceCell as SyncOnceCell; 592 | pub use proc_macro2::Span; 593 | pub use std::{ 594 | borrow::{Borrow, Cow, ToOwned}, 595 | clone::Clone, 596 | collections::HashMap, 597 | convert::{AsRef, From}, 598 | default::Default, 599 | format, format_args, 600 | iter::IntoIterator, 601 | option::Option, 602 | primitive::{bool, str, usize}, 603 | string::{String, ToString}, 604 | unreachable, 605 | vec::Vec, 606 | }; 607 | pub use syn::{ 608 | parse::{ParseBuffer, ParseStream}, 609 | Error, Ident, Path, 610 | }; 611 | } 612 | 613 | pub use deluxe_core::{ 614 | define_with_collection, define_with_map, define_with_optional, parse_named_meta_item_with, 615 | with, Error, Errors, ExtractAttributes, Flag, HasAttributes, ParseAttributes, ParseMetaItem, 616 | ParseMode, Result, SpannedValue, 617 | }; 618 | #[doc(hidden)] 619 | pub use deluxe_core::{ 620 | ContainerFrom, ParseMetaAppend, ParseMetaFlatNamed, ParseMetaFlatUnnamed, ParseMetaRest, 621 | ToKeyString, 622 | }; 623 | pub use deluxe_macros::*; 624 | 625 | /// Additional helper functions for validating after parsing. 626 | pub mod validations { 627 | pub use deluxe_core::validations::*; 628 | pub use deluxe_core::{all_or_none, only_one}; 629 | } 630 | 631 | #[cfg(feature = "proc-macro")] 632 | extern crate proc_macro; 633 | 634 | /// Parses a required Rust type out of a token stream. 635 | /// 636 | /// Intended for use with [attribute 637 | /// macros](https://doc.rust-lang.org/stable/reference/procedural-macros.html#attribute-macros). 638 | /// This is a small wrapper around [`syn::parse::Parser::parse`] and 639 | /// [`ParseMetaItem::parse_meta_item_inline`]. 640 | #[cfg(feature = "proc-macro")] 641 | #[inline] 642 | pub fn parse(attr: proc_macro::TokenStream) -> Result { 643 | syn::parse::Parser::parse( 644 | |stream: syn::parse::ParseStream<'_>| { 645 | T::parse_meta_item_inline(&[stream], ParseMode::Named(proc_macro2::Span::call_site())) 646 | }, 647 | attr, 648 | ) 649 | } 650 | 651 | /// Parses a required Rust type out of a token stream. 652 | /// 653 | /// Intended for use with [attribute 654 | /// macros](https://doc.rust-lang.org/stable/reference/procedural-macros.html#attribute-macros). 655 | /// This is a small wrapper around [`syn::parse::Parser::parse2`] and 656 | /// [`ParseMetaItem::parse_meta_item_inline`]. 657 | #[inline] 658 | pub fn parse2(attr: proc_macro2::TokenStream) -> Result { 659 | syn::parse::Parser::parse2( 660 | |stream: syn::parse::ParseStream<'_>| { 661 | T::parse_meta_item_inline(&[stream], ParseMode::Named(proc_macro2::Span::call_site())) 662 | }, 663 | attr, 664 | ) 665 | } 666 | 667 | /// Parses a required Rust type out of another type holding a list of [`syn::Attribute`]. 668 | /// 669 | /// Intended for use with [derive 670 | /// macros](https://doc.rust-lang.org/stable/reference/procedural-macros.html#derive-macros). This 671 | /// is a small wrapper around [`ParseAttributes::parse_attributes`]. 672 | #[inline] 673 | pub fn parse_attributes<'t, T, R>(obj: &'t T) -> Result 674 | where 675 | T: HasAttributes, 676 | R: ParseAttributes<'t, T>, 677 | { 678 | R::parse_attributes(obj) 679 | } 680 | 681 | /// Extracts attributes out of another type holding a list of [`syn::Attribute`], then parses them 682 | /// into a required Rust type. 683 | /// 684 | /// Intended for use with [derive 685 | /// macros](https://doc.rust-lang.org/stable/reference/procedural-macros.html#derive-macros). This 686 | /// is a small wrapper around [`ExtractAttributes::extract_attributes`]. 687 | #[inline] 688 | pub fn extract_attributes(obj: &mut T) -> Result 689 | where 690 | T: HasAttributes, 691 | R: ExtractAttributes, 692 | { 693 | R::extract_attributes(obj) 694 | } 695 | 696 | /// Parses a Rust type out of a token stream, returning a default value on failure. 697 | /// 698 | /// Calls [`parse`] and returns the result. Upon failure, [`Default::default`] will be returned and 699 | /// any errors will be appended to `errors`. This function should be preferred when parsing 700 | /// multiple attributes, so the errors from all of them can be accumulated instead of returning 701 | /// early. 702 | #[cfg(feature = "proc-macro")] 703 | #[inline] 704 | pub fn parse_optional( 705 | attr: proc_macro::TokenStream, 706 | errors: &Errors, 707 | ) -> T { 708 | match parse(attr) { 709 | Ok(t) => t, 710 | Err(e) => { 711 | errors.push_syn(e); 712 | Default::default() 713 | } 714 | } 715 | } 716 | 717 | /// Parses a Rust type out of a token stream, returning a default value on failure. 718 | /// 719 | /// Calls [`parse2`] and returns the result. Upon failure, [`Default::default`] will be returned 720 | /// and any errors will be appended to `errors`. This function should be preferred when parsing 721 | /// multiple attributes, so the errors from all of them can be accumulated instead of returning 722 | /// early. 723 | #[inline] 724 | pub fn parse2_optional( 725 | attr: proc_macro2::TokenStream, 726 | errors: &Errors, 727 | ) -> T { 728 | match parse2(attr) { 729 | Ok(t) => t, 730 | Err(e) => { 731 | errors.push_syn(e); 732 | Default::default() 733 | } 734 | } 735 | } 736 | 737 | /// Parses a Rust type out of another type holding a list of [`syn::Attribute`], returning a default value on failure. 738 | /// 739 | /// Calls [`parse_attributes`] and returns the result. Upon failure, [`Default::default`] will be 740 | /// returned and any errors will be appended to `errors`. This function should be preferred when 741 | /// parsing multiple attributes, so the errors from all of them can be accumulated instead of 742 | /// returning early. 743 | #[inline] 744 | pub fn parse_attributes_optional<'t, T, R>(obj: &'t T, errors: &Errors) -> R 745 | where 746 | T: HasAttributes, 747 | R: ParseAttributes<'t, T> + Default, 748 | { 749 | match parse_attributes(obj) { 750 | Ok(t) => t, 751 | Err(e) => { 752 | errors.push_syn(e); 753 | Default::default() 754 | } 755 | } 756 | } 757 | 758 | /// Extracts attributes out of another type holding a list of [`syn::Attribute`], then parses them 759 | /// into a Rust type, returning a default value on failure. 760 | /// 761 | /// Calls [`extract_attributes`] and returns the result. Upon failure, [`Default::default`] will be 762 | /// returned and any errors will be appended to `errors`. This function should be preferred when 763 | /// parsing multiple attributes, so the errors from all of them can be accumulated instead of 764 | /// returning early. 765 | #[inline] 766 | pub fn extract_attributes_optional(obj: &mut T, errors: &Errors) -> R 767 | where 768 | T: HasAttributes, 769 | R: ExtractAttributes + Default, 770 | { 771 | match extract_attributes(obj) { 772 | Ok(t) => t, 773 | Err(e) => { 774 | errors.push_syn(e); 775 | Default::default() 776 | } 777 | } 778 | } 779 | -------------------------------------------------------------------------------- /tests/attributes.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_code)] 2 | #![no_implicit_prelude] 3 | 4 | use ::quote::quote as q; 5 | 6 | mod test_util; 7 | use test_util::*; 8 | 9 | #[derive(::deluxe::ParseAttributes, ::deluxe::ExtractAttributes, PartialEq, Debug)] 10 | #[deluxe(attributes(single))] 11 | struct SingleAttribute(char); 12 | 13 | #[derive(::deluxe::ParseAttributes, ::deluxe::ExtractAttributes, PartialEq, Debug)] 14 | #[deluxe(attributes(single))] 15 | struct SingleAttributeNamed { 16 | c: char, 17 | } 18 | 19 | #[derive(::deluxe::ParseAttributes, ::deluxe::ExtractAttributes, PartialEq, Debug)] 20 | #[deluxe(attributes(multi1, multi2))] 21 | struct MultiAttributes(char); 22 | 23 | #[test] 24 | fn multi_attributes() { 25 | use ::deluxe::HasAttributes; 26 | 27 | let expr: ::syn::Expr = ::syn::parse2(q! { #[single('a')] true }).unwrap(); 28 | let m: SingleAttribute = ::deluxe::parse_attributes(&expr).unwrap(); 29 | ::std::assert_eq!(expr.attrs().len(), 1); 30 | ::std::assert_eq!(m.0, 'a'); 31 | 32 | let expr: ::syn::Expr = ::syn::parse2(q! { #[single(c = 'a')] true }).unwrap(); 33 | let m: SingleAttributeNamed = ::deluxe::parse_attributes(&expr).unwrap(); 34 | ::std::assert_eq!(expr.attrs().len(), 1); 35 | ::std::assert_eq!(m.c, 'a'); 36 | 37 | let expr: ::syn::Expr = ::syn::parse2(q! { true }).unwrap(); 38 | ::std::assert_eq!( 39 | ::deluxe::parse_attributes::<::syn::Expr, SingleAttribute>(&expr).unwrap_err_string(), 40 | "missing required field 0 on #[single]" 41 | ); 42 | 43 | let expr: ::syn::Expr = ::syn::parse2(q! { #[single] true }).unwrap(); 44 | ::std::assert_eq!( 45 | ::deluxe::parse_attributes::<::syn::Expr, SingleAttribute>(&expr).unwrap_err_string(), 46 | "missing required field 0 on #[single]" 47 | ); 48 | 49 | let expr: ::syn::Expr = ::syn::parse2(q! { true }).unwrap(); 50 | ::std::assert_eq!( 51 | ::deluxe::parse_attributes::<::syn::Expr, SingleAttributeNamed>(&expr).unwrap_err_string(), 52 | "missing required field #[single(c)]" 53 | ); 54 | 55 | let expr: ::syn::Expr = ::syn::parse2(q! { #[single] true }).unwrap(); 56 | ::std::assert_eq!( 57 | ::deluxe::parse_attributes::<::syn::Expr, SingleAttributeNamed>(&expr).unwrap_err_string(), 58 | "missing required field #[single(c)]" 59 | ); 60 | 61 | let expr: ::syn::Expr = ::syn::parse2(q! { #[multi1('a')] true }).unwrap(); 62 | let m: MultiAttributes = ::deluxe::parse_attributes(&expr).unwrap(); 63 | ::std::assert_eq!(expr.attrs().len(), 1); 64 | ::std::assert_eq!(m.0, 'a'); 65 | 66 | let expr: ::syn::Expr = ::syn::parse2(q! { #[multi2('b')] true }).unwrap(); 67 | let m: MultiAttributes = ::deluxe::parse_attributes(&expr).unwrap(); 68 | ::std::assert_eq!(expr.attrs().len(), 1); 69 | ::std::assert_eq!(m.0, 'b'); 70 | 71 | let expr = ::syn::parse2(q! { true }).unwrap(); 72 | ::std::assert_eq!( 73 | ::deluxe::parse_attributes::<::syn::Expr, MultiAttributes>(&expr).unwrap_err_string(), 74 | "missing required field 0 on #[multi1]" 75 | ); 76 | 77 | let expr = ::syn::parse2(q! { #[multi1()] true }).unwrap(); 78 | ::std::assert_eq!( 79 | ::deluxe::parse_attributes::<::syn::Expr, MultiAttributes>(&expr).unwrap_err_string(), 80 | "missing required field 0 on #[multi1]" 81 | ); 82 | 83 | let expr = ::syn::parse2(q! { #[multi2()] true }).unwrap(); 84 | ::std::assert_eq!( 85 | ::deluxe::parse_attributes::<::syn::Expr, MultiAttributes>(&expr).unwrap_err_string(), 86 | "missing required field 0 on #[multi2]" 87 | ); 88 | 89 | let expr = ::syn::parse2(q! { #[multi2] true }).unwrap(); 90 | ::std::assert_eq!( 91 | ::deluxe::parse_attributes::<::syn::Expr, MultiAttributes>(&expr).unwrap_err_string(), 92 | "missing required field 0 on #[multi2]" 93 | ); 94 | 95 | let expr = ::syn::parse2(q! { #[multi3('c')] true }).unwrap(); 96 | ::std::assert_eq!( 97 | ::deluxe::parse_attributes::<::syn::Expr, MultiAttributes>(&expr).unwrap_err_string(), 98 | "missing required field 0 on #[multi1]" 99 | ); 100 | 101 | let expr = ::syn::parse2(q! { #[multi2('c')] #[multi2('d')] true }).unwrap(); 102 | ::std::assert_eq!( 103 | ::deluxe::parse_attributes::<::syn::Expr, MultiAttributes>(&expr).unwrap_err_string(), 104 | "unexpected token" 105 | ); 106 | } 107 | 108 | #[derive(::deluxe::ParseAttributes, ::deluxe::ExtractAttributes, PartialEq, Debug)] 109 | #[deluxe(attributes(many))] 110 | struct Many(i32, i32, i32, i32, i32, i32); 111 | 112 | #[derive(::deluxe::ParseAttributes, ::deluxe::ExtractAttributes, PartialEq, Debug)] 113 | #[deluxe(attributes(many), transparent)] 114 | struct ManyVec(::std::vec::Vec); 115 | 116 | #[test] 117 | fn split_attributes() { 118 | let expr: ::syn::Expr = 119 | ::syn::parse2(q! { #[many(1)] #[many(2, 3)] #[many(4, 5, 6)] true }).unwrap(); 120 | let m: Many = ::deluxe::parse_attributes(&expr).unwrap(); 121 | ::std::assert_eq!(m, Many(1, 2, 3, 4, 5, 6)); 122 | 123 | let expr: ::syn::Expr = 124 | ::syn::parse2(q! { #[many(1)] #[many(2, 3)] #[many(4, 5, 6)] true }).unwrap(); 125 | let m: ManyVec = ::deluxe::parse_attributes(&expr).unwrap(); 126 | ::std::assert_eq!(m, ManyVec(::std::vec![1, 2, 3, 4, 5, 6])); 127 | } 128 | 129 | #[derive(::deluxe::ParseAttributes, ::deluxe::ExtractAttributes, PartialEq, Debug)] 130 | #[deluxe(attributes(container::clone))] 131 | struct CloneContainer { 132 | #[deluxe(container)] 133 | container: ::syn::Expr, 134 | int: i32, 135 | } 136 | 137 | #[test] 138 | fn clone_container() { 139 | use ::deluxe::HasAttributes; 140 | let mut expr = ::syn::parse2(q! { 141 | #[container::ignored(int = 4)] 142 | #[container::clone(int = 2)] 143 | true 144 | }) 145 | .unwrap(); 146 | let cont: CloneContainer = ::deluxe::parse_attributes(&expr).unwrap(); 147 | ::std::assert_eq!(expr.attrs().len(), 2); 148 | ::std::assert_eq!(cont.container, expr); 149 | ::std::assert_eq!(cont.int, 2); 150 | let cont2: CloneContainer = ::deluxe::extract_attributes(&mut expr).unwrap(); 151 | ::std::assert_eq!(expr.attrs().len(), 1); 152 | ::std::assert_eq!(cont2.container, expr); 153 | ::std::assert_eq!(cont2.int, 2); 154 | ::std::assert_ne!(cont.container, cont2.container); 155 | } 156 | 157 | #[derive(::deluxe::ParseAttributes, PartialEq, Debug)] 158 | #[deluxe(attributes(container::r#ref))] 159 | struct RefContainer<'e> { 160 | #[deluxe(container)] 161 | container: &'e ::syn::Expr, 162 | int: i32, 163 | } 164 | 165 | #[test] 166 | fn ref_container() { 167 | let expr = ::syn::parse2(q! { #[container::r#ref(int = 3)] true }).unwrap(); 168 | let cont: RefContainer = ::deluxe::parse_attributes(&expr).unwrap(); 169 | ::std::assert_eq!(cont.container, &expr); 170 | ::std::assert_eq!(cont.int, 3); 171 | } 172 | 173 | #[derive(::deluxe::ParseAttributes, ::deluxe::ExtractAttributes, PartialEq, Debug)] 174 | #[deluxe(attributes(container::clone_generic))] 175 | struct CloneGenericContainer { 176 | #[deluxe(container)] 177 | container: T, 178 | int: i32, 179 | } 180 | 181 | #[test] 182 | fn clone_generic_container() { 183 | let mut expr = ::syn::parse2(q! { #[container::clone_generic(int = 4)] true }).unwrap(); 184 | let cont: CloneGenericContainer<::syn::Expr> = ::deluxe::parse_attributes(&expr).unwrap(); 185 | ::std::assert_eq!(cont.container, expr); 186 | ::std::assert_eq!(cont.int, 4); 187 | let cont2: CloneGenericContainer<::syn::Expr> = 188 | ::deluxe::extract_attributes(&mut expr).unwrap(); 189 | ::std::assert_eq!(cont2.container, expr); 190 | ::std::assert_eq!(cont2.int, 4); 191 | } 192 | 193 | #[derive(::deluxe::ParseAttributes, PartialEq, Debug)] 194 | #[deluxe(attributes(container::ref_generic))] 195 | struct RefGenericContainer<'t, T> { 196 | #[deluxe(container)] 197 | container: &'t T, 198 | int: i32, 199 | } 200 | 201 | #[test] 202 | fn ref_generic_container() { 203 | let expr = ::syn::parse2(q! { #[container::ref_generic(int = 5)] true }).unwrap(); 204 | let cont: RefGenericContainer<::syn::Expr> = ::deluxe::parse_attributes(&expr).unwrap(); 205 | ::std::assert_eq!(cont.container, &expr); 206 | ::std::assert_eq!(cont.int, 5); 207 | } 208 | 209 | #[derive( 210 | ::deluxe::ParseAttributes, 211 | ::deluxe::ExtractAttributes, 212 | ::deluxe::ParseMetaItem, 213 | PartialEq, 214 | Debug, 215 | )] 216 | #[deluxe(attributes(container::clone_option))] 217 | struct CloneOptionContainer { 218 | #[deluxe(container, default)] 219 | container: ::std::option::Option<::syn::Expr>, 220 | int: i32, 221 | } 222 | 223 | #[test] 224 | fn clone_option_container() { 225 | let mut expr = ::syn::parse2(q! { #[container::clone_option(int = 6)] true }).unwrap(); 226 | let cont: CloneOptionContainer = ::deluxe::parse_attributes(&expr).unwrap(); 227 | ::std::assert_eq!(cont.container.unwrap(), expr); 228 | ::std::assert_eq!(cont.int, 6); 229 | let cont2: CloneOptionContainer = ::deluxe::extract_attributes(&mut expr).unwrap(); 230 | ::std::assert_eq!(cont2.container, ::std::option::Option::Some(expr)); 231 | ::std::assert_eq!(cont2.int, 6); 232 | } 233 | 234 | #[derive(::deluxe::ParseAttributes, ::deluxe::ParseMetaItem, PartialEq, Debug)] 235 | #[deluxe(attributes(container::ref_option))] 236 | struct RefOptionContainer<'e> { 237 | #[deluxe(container, default)] 238 | container: ::std::option::Option<&'e ::syn::Expr>, 239 | int: i32, 240 | } 241 | 242 | #[test] 243 | fn ref_option_container() { 244 | let expr = ::syn::parse2(q! { #[container::ref_option(int = 7)] true }).unwrap(); 245 | let cont: RefOptionContainer = ::deluxe::parse_attributes(&expr).unwrap(); 246 | ::std::assert_eq!(cont.container, ::std::option::Option::Some(&expr)); 247 | ::std::assert_eq!(cont.int, 7); 248 | } 249 | 250 | #[derive( 251 | ::deluxe::ParseAttributes, 252 | ::deluxe::ExtractAttributes, 253 | ::deluxe::ParseMetaItem, 254 | PartialEq, 255 | Debug, 256 | )] 257 | #[deluxe(attributes(container::clone_generic_option))] 258 | struct CloneGenericOptionContainer { 259 | #[deluxe(container, default)] 260 | container: ::std::option::Option, 261 | int: i32, 262 | } 263 | 264 | #[test] 265 | fn clone_generic_option_container() { 266 | let mut expr = ::syn::parse2(q! { #[container::clone_generic_option(int = 8)] true }).unwrap(); 267 | let cont: CloneGenericOptionContainer<::syn::Expr> = ::deluxe::parse_attributes(&expr).unwrap(); 268 | ::std::assert_eq!(cont.container.unwrap(), expr); 269 | ::std::assert_eq!(cont.int, 8); 270 | let cont2: CloneGenericOptionContainer<::syn::Expr> = 271 | ::deluxe::extract_attributes(&mut expr).unwrap(); 272 | ::std::assert_eq!(cont2.container, ::std::option::Option::Some(expr)); 273 | ::std::assert_eq!(cont2.int, 8); 274 | } 275 | 276 | #[derive(::deluxe::ParseAttributes, ::deluxe::ParseMetaItem, PartialEq, Debug)] 277 | #[deluxe(attributes(container::ref_generic_option))] 278 | struct RefGenericOptionContainer<'t, T> { 279 | #[deluxe(container, default)] 280 | container: ::std::option::Option<&'t T>, 281 | int: i32, 282 | } 283 | 284 | #[test] 285 | fn ref_generic_option_container() { 286 | let expr = ::syn::parse2(q! { #[container::ref_generic_option(int = 9)] true }).unwrap(); 287 | let cont: RefGenericOptionContainer<::syn::Expr> = ::deluxe::parse_attributes(&expr).unwrap(); 288 | ::std::assert_eq!(cont.container, ::std::option::Option::Some(&expr)); 289 | ::std::assert_eq!(cont.int, 9); 290 | } 291 | 292 | #[derive(::deluxe::ParseAttributes, ::deluxe::ExtractAttributes, PartialEq, Debug)] 293 | #[deluxe(attributes(container::an_enum))] 294 | enum ContainerEnum { 295 | A(#[deluxe(container)] ::syn::Expr, #[deluxe(skip)] i32, i32), 296 | B { 297 | #[deluxe(container)] 298 | container: ::syn::Expr, 299 | #[deluxe(skip)] 300 | skipped: i32, 301 | value: i32, 302 | }, 303 | #[deluxe(flatten)] 304 | C { 305 | #[deluxe(container)] 306 | container: ::syn::Expr, 307 | #[deluxe(skip)] 308 | skipped: i32, 309 | c: i32, 310 | }, 311 | #[deluxe(flatten)] 312 | D { 313 | #[deluxe(container)] 314 | container: ::syn::Expr, 315 | #[deluxe(skip)] 316 | skipped: i32, 317 | }, 318 | } 319 | 320 | #[test] 321 | fn container_enum() { 322 | let expr: ::syn::Expr = ::syn::parse2(q! { 323 | #[container::an_enum(a(500))] 324 | true 325 | }) 326 | .unwrap(); 327 | let cont: ContainerEnum = ::deluxe::parse_attributes(&expr).unwrap(); 328 | ::std::assert!(::std::matches!(cont, ContainerEnum::A(e, 0, 500) if e == expr)); 329 | 330 | let expr: ::syn::Expr = ::syn::parse2(q! { 331 | #[container::an_enum(b(value = 501))] 332 | true 333 | }) 334 | .unwrap(); 335 | let cont: ContainerEnum = ::deluxe::parse_attributes(&expr).unwrap(); 336 | ::std::assert!( 337 | ::std::matches!(cont, ContainerEnum::B { container: e, skipped: 0, value: 501 } if e == expr) 338 | ); 339 | 340 | let expr: ::syn::Expr = ::syn::parse2(q! { 341 | #[container::an_enum(c = 502)] 342 | true 343 | }) 344 | .unwrap(); 345 | let cont: ContainerEnum = ::deluxe::parse_attributes(&expr).unwrap(); 346 | ::std::assert!( 347 | ::std::matches!(cont, ContainerEnum::C { container: e, skipped: 0, c: 502 } if e == expr) 348 | ); 349 | 350 | let expr: ::syn::Expr = ::syn::parse2(q! { 351 | #[container::an_enum()] 352 | true 353 | }) 354 | .unwrap(); 355 | let cont: ContainerEnum = ::deluxe::parse_attributes(&expr).unwrap(); 356 | ::std::assert!( 357 | ::std::matches!(cont, ContainerEnum::D { container: e, skipped: 0 } if e == expr) 358 | ); 359 | } 360 | 361 | #[derive(Debug, PartialEq)] 362 | struct IdentWrapper<'t>(&'t ::syn::Ident); 363 | 364 | impl<'t> ::deluxe::ContainerFrom<'t, ::syn::ItemFn> for IdentWrapper<'t> { 365 | #[inline] 366 | fn container_from(t: &'t ::syn::ItemFn) -> Self { 367 | Self(&t.sig.ident) 368 | } 369 | } 370 | 371 | #[derive(::deluxe::ParseAttributes, PartialEq, Debug)] 372 | #[deluxe(attributes(container::wrapper))] 373 | struct WrapperContainer<'t> { 374 | #[deluxe(container(ty = ::syn::ItemFn))] 375 | container: IdentWrapper<'t>, 376 | int: i32, 377 | } 378 | 379 | #[derive(Debug, PartialEq)] 380 | struct Ident2Wrapper<'s, 't>(::std::borrow::Cow<'s, str>, &'t ::syn::Ident); 381 | 382 | impl<'s, 't> ::deluxe::ContainerFrom<'t, ::syn::ItemFn> for Ident2Wrapper<'s, 't> { 383 | #[inline] 384 | fn container_from(t: &'t ::syn::ItemFn) -> Self { 385 | Self(::std::convert::From::from("hello"), &t.sig.ident) 386 | } 387 | } 388 | 389 | #[derive(::deluxe::ParseAttributes, PartialEq, Debug)] 390 | #[deluxe(attributes(container::wrapper))] 391 | struct Wrapper2Container<'s, 't> { 392 | #[deluxe(container(ty = ::syn::ItemFn, lifetime = 't))] 393 | container: Ident2Wrapper<'s, 't>, 394 | int: i32, 395 | } 396 | 397 | #[test] 398 | fn custom_container() { 399 | use ::deluxe::HasAttributes; 400 | let func: ::syn::ItemFn = ::syn::parse2(q! { 401 | #[container::wrapper(int = 72)] 402 | fn abc() -> i32 { 0 } 403 | }) 404 | .unwrap(); 405 | let cont: WrapperContainer = ::deluxe::parse_attributes(&func).unwrap(); 406 | ::std::assert_eq!(func.attrs().len(), 1); 407 | ::std::assert_eq!(cont.container.0, &func.sig.ident); 408 | ::std::assert_eq!(cont.int, 72); 409 | 410 | let cont: Wrapper2Container = ::deluxe::parse_attributes(&func).unwrap(); 411 | ::std::assert_eq!(func.attrs().len(), 1); 412 | ::std::assert_eq!(cont.container.0, "hello"); 413 | ::std::assert_eq!(cont.container.1, &func.sig.ident); 414 | ::std::assert_eq!(cont.int, 72); 415 | } 416 | -------------------------------------------------------------------------------- /tests/test_util/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use ::proc_macro2::TokenStream; 4 | use ::std::prelude::v1::*; 5 | 6 | pub trait SynResultExt { 7 | fn unwrap_err_string(self) -> String; 8 | } 9 | 10 | impl SynResultExt for ::syn::Result { 11 | fn unwrap_err_string(self) -> String { 12 | self.unwrap_err() 13 | .into_iter() 14 | .map(|e| e.to_string()) 15 | .collect::>() 16 | .join(", ") 17 | } 18 | } 19 | 20 | #[inline] 21 | pub fn parse_meta(s: TokenStream) -> ::deluxe::Result { 22 | use ::syn::parse::Parser; 23 | let parser = |stream: ::syn::parse::ParseStream<'_>| { 24 | ::parse_meta_item(stream, ::deluxe::ParseMode::Unnamed) 25 | }; 26 | parser.parse2(s) 27 | } 28 | 29 | #[inline] 30 | pub fn parse_flag() -> ::deluxe::Result { 31 | use ::syn::parse::Parser; 32 | let parser = |stream: ::syn::parse::ParseStream<'_>| { 33 | ::parse_meta_item_flag(stream.span()) 34 | }; 35 | parser.parse2(Default::default()) 36 | } 37 | --------------------------------------------------------------------------------