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

Feature Request: Get the field names of a struct #19

Closed
ta32 opened this issue Jun 16, 2021 · 9 comments
Closed

Feature Request: Get the field names of a struct #19

ta32 opened this issue Jun 16, 2021 · 9 comments

Comments

@ta32
Copy link
Contributor

ta32 commented Jun 16, 2021

Hi

Would a function implemented in this issue, be useful in this crate?
serde-rs/serde#1110

fn main() {
    // prints ["error", "description"]
    println!("{:?}", struct_fields::<AuthError>());
}

A function like this would eliminate a lot of boiler plate code in my app. I copied and pasted the implementation into my application and it works but I think it would be useful as apart of this library. The original issue was closed.

Note: link has implementation for enum and struct

@iddm
Copy link
Owner

iddm commented Aug 11, 2021

Do you think this crate might be of use? https://github.com/vityafx/introspection

@ta32
Copy link
Contributor Author

ta32 commented Aug 12, 2021

I should have mentioned that the function in serde-rs/serde#1110, will get the field names as they are serialized - including the effect of macros. Eg

#[macro_use] extern crate serde;
#[macro_use] extern crate serde_derive;

use serde::de::{self, Deserialize, Deserializer, Visitor};

#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
enum Setting { On, Off }

fn enum_variants<'de, T>() -> &'static [&'static str]
    where T: Deserialize<'de>
{
    struct EnumVariantsDeserializer<'a> {
        variants: &'a mut Option<&'static [&'static str]>,
    }

    impl<'de, 'a> Deserializer<'de> for EnumVariantsDeserializer<'a> {
        type Error = serde::de::value::Error;

        fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
            where V: Visitor<'de>
        {
            Err(de::Error::custom("I'm just here for the variants"))
        }

        fn deserialize_enum<V>(
            self,
            _name: &'static str,
            variants: &'static [&'static str],
            visitor: V
        ) -> Result<V::Value, Self::Error>
            where V: Visitor<'de>
        {
            *self.variants = Some(variants);
            self.deserialize_any(visitor)
        }

        forward_to_deserialize_any! {
            bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
            byte_buf option unit unit_struct newtype_struct seq tuple
            tuple_struct map struct identifier ignored_any
        }
    }

    let mut variants = None;
    let _ = T::deserialize(EnumVariantsDeserializer { variants: &mut variants });
    variants.unwrap()
}

fn main() {
    // prints ["on", "off"]
    println!("{:?}", enum_variants::<Setting>());
}

Where as introspection gets the field names generically. Eg because the Setting enum has "#[serde(rename_all = "lowercase")]" the field names will be different.

@iddm
Copy link
Owner

iddm commented Aug 12, 2021

I am still struggling with understanding the use-case, but that is something you could answer, I guess. If you think it helps, then yes, let's go add it. Would you like to do this or would you like me to do that myself?

@ta32
Copy link
Contributor Author

ta32 commented Aug 14, 2021

I was basically querying an API and de-serializing the results into a vector of structs.

The API needs a query selecting all the field names of the struct, matching how they would be serialized.

For some of the fields I used directives like #[serde(rename = "FIELDNAME")]
As the struct member would be field_name.

I created a method for the struct which calls:
let fields = struct_fields::<Self>().to_vec();

This returns a list of field names as they would be serialized/de-serialized by serde.

I can have ago at creating a PR if you want. I guess I would need to create some tests and some usage comments?

@iddm
Copy link
Owner

iddm commented Aug 14, 2021

Thanks for clarifying the intent and the use case. Yes please, go ahead if you have time. And yes, please, try to provide such doc-comments with tests and usage.

@ta32
Copy link
Contributor Author

ta32 commented Aug 29, 2021

@vityafx
I checked out your repo and created a branch where I implemented the changes, but I cant push my branch to remote.

This is my first time contributing to open source so I am not sure about the correct procedure.
Do I need to fork the project and merge the code there?

@iddm
Copy link
Owner

iddm commented Aug 29, 2021

@ta32 Yes, this is how it works. First, you fork a repository so that GitHub knows the connection between a fork and the upstream repository. Then you work with the fork by cloning it and making changes there, and then you open a pull request proposing your changes to be merged to the upstream.

@iddm
Copy link
Owner

iddm commented Sep 8, 2021

Done in #20

@iddm iddm closed this as completed Sep 8, 2021
@iddm
Copy link
Owner

iddm commented Sep 8, 2021

I have just published 2.3.0 with your changes.

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

No branches or pull requests

2 participants