Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missed documentation about how to use derives #165

Closed
Mingun opened this issue Mar 22, 2022 · 2 comments
Closed

Missed documentation about how to use derives #165

Mingun opened this issue Mar 22, 2022 · 2 comments

Comments

@Mingun
Copy link

Mingun commented Mar 22, 2022

I expect to find an example of an user struct with From* trait derived and example of the rust code that it will parse.

I want to create a simple filter for #[derive(serde::Deserialize)] and convert this:

#[quick_xml]
#[derive(serde::Deserialize)]
struct XmlType {
  #[xml(attribute)]
  field: (),
}

to this:

#[derive(serde::Deserialize)]
struct XmlType {
  #[serde(rename = "@field")]
  field: (),
}

(i.e. generate a serde rename for all fields with an #[xml(attribute)] by prepending to their names an @ symbol)

My current code is:

use darling::FromField;
use syn::{parse_macro_input, Data, DeriveInput, Fields};
use proc_macro::TokenStream;
use quote::ToTokens;

/// Attributes of the `#[xml(...)]` macro
#[derive(Debug, Default, FromField)]
#[darling(default)]
struct FieldProps {
  /// Field should be stored in an XML attribute.
  /// Appends `@` before a field name.
  ///
  /// `#[xml(attribute)]`
  attribute: bool,

  /// XML namespace associated with a field.
  /// Appends `{namespace}` before a field name
  ///
  /// `#[xml(namespace = ...)]`
  namespace: Option<String>,
}

#[proc_macro_attribute]
pub fn quick_xml(_attr: TokenStream, item: TokenStream) -> TokenStream {
  let ast = parse_macro_input!(item as DeriveInput);

  if let Data::Struct(s) = &ast.data {
    if let Fields::Named(fields) = &s.fields {
      for f in &fields.named {
        let props = FieldProps::from_field(&f);

        println!("  props: {:?}", props);
      }
    }
  }

  ast.into_token_stream().into()
}

but props is obviously does not contain what I want because I even do not specify that I want to process xml attributes. Where I should do this?


I want to get answer to all that questions from the documentation without any third-party support. Can you please add such a documentation.


Then I found #156, it answer on some questions, for example, how to tell what attribute is interested:

/// Attributes of the `#[xml(...)]` macro
#[derive(Debug, Default, FromField)]
#[darling(default, attributes(xml))]  //<<<<<<<<<<<<<   Added `attributes(xml)`
struct FieldProps {
  /// Field should be stored in an XML attribute.
  /// Appends `@` before a field name.
  ///
  /// `#[xml(attribute)]`
  attribute: bool,

  /// XML namespace associated with a field.
  /// Appends `{namespace}` before a field name
  ///
  /// `#[xml(namespace = ...)]`
  namespace: Option<String>,
}

But problem in that that props always Ok(), even when no attributes are present on the field. How I can check that at least xml attribute is present?

Ideally, I also want to have a mutually exclusive attributes:

/// Attributes of the `#[xml(...)]` macro
#[derive(Debug, Default, FromField)]
#[darling(default, attributes(xml))]
struct FieldProps {
  /// Field should be stored in an XML attribute.
  /// Appends `@` before a field name.
  ///
  /// `#[xml(attribute)]`
  attribute: bool,

  /// XML namespace associated with a field.
  /// Appends `{namespace}` before a field name
  ///
  /// `#[xml(namespace = ...)]`
  namespace: Option<String>,

  /// Field should be stored in the first `#text` or `#cdata` node.
  /// Set field name to `$value`
  ///
  /// `#[xml(content)]`
  content: bool,

  /// Workaround for buggy and not full-feature `#[serde(flatten)]`.
  /// Emulates flatten on deserializer level.
  /// Appends `$flatten=` before a field name
  ///
  /// `#[xml(flatten)]`
  flatten: bool,
}

attribute and content, attribute and flatten are mutually exclusive, I want to emit an error if both are present. Could I change FieldProps to enum for this? If yes, what the additional steps I should perform to parse such attributes?


It also would be useful, if documentation for each trait will show what code is generated by derives (with cuts if required).


I found that examples in the examples folder definitely missing an examples of input which struct could handle, for example, here:

/// A speaking volume. Deriving `FromMeta` will cause this to be usable
/// as a string value for a meta-item key.
#[derive(Debug, Clone, Copy, FromMeta)]
#[darling(default)]
enum Volume {
Normal,
Whisper,
Shout,
}

If I understand correctly, that enum could be parsed from ... = "Normal", ... = "Whisper", or ... = "Shout".
If so, this will be useful addition to the documentation.
The similar examples needed for all other structs that derives darling From* traits.

@TedDriggs
Copy link
Owner

But problem in that that props always Ok(), even when no attributes are present on the field. How I can check that at least xml attribute is present?

This isn't something darling provides natively; I'd suggest using #[xml(skip)] or having #[xml(element)] in addition to #[xml(attribute)] so that you can tell the difference between them.

attribute and content, attribute and flatten are mutually exclusive, I want to emit an error if both are present. Could I change FieldProps to enum for this? If yes, what the additional steps I should perform to parse such attributes?

It also would be useful, if documentation for each trait will show what code is generated by derives (with cuts if required).

This is much too dependent on the struct/enum in question. I'd suggest using cargo expand to see how darling has expanded a particular receiver.

If you can't get the enum to do what you want, then I'd suggest using #[darling(and_then = "...")] to do validation after attribute parsing; you can use receivers such as LitBool to preserve span information for good errors if you go that route.


I'm closing this issue - while more examples would be useful, this issue is asking a lot of questions that are specific for a particular use-case. I would be open to PRs with added examples that showcase specific functionality, or one that uses expect_test or something similar to automate producing updated generated code for test-cases.

@Mingun
Copy link
Author

Mingun commented Mar 23, 2022

This isn't something darling provides natively; I'd suggest using #[xml(skip)] or having #[xml(element)] in addition to #[xml(attribute)] so that you can tell the difference between them.

I don't see how this helps -- I need to distinquish this situations:

// #[xml(...)] present
#[xml(attribute)]
field: (),

and

// #[xml(...)] does not present
field: (),

I think it's darling responsibility


I'm closing this issue

Generally, this issue about missing documentation. My use-case just an illustration, so you can understand what problems I faced -- I even don't know which derive I should use, because their purpose isn't described and there are missing examples of their usage (BOTH the complete compilable code and the input). That is what I'm asking to add. Because I don't see any similar open issue, I've opened this and I think it should stay open until that documentation will be written

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants