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

Get the field names of a struct #1110

Closed
dtolnay opened this issue Dec 3, 2017 · 3 comments
Closed

Get the field names of a struct #1110

dtolnay opened this issue Dec 3, 2017 · 3 comments
Labels

Comments

@dtolnay
Copy link
Member

dtolnay commented Dec 3, 2017

A question from u.r-l.o: how to get a slice of field names for an arbitrary struct type T?

@dtolnay dtolnay added the support label Dec 3, 2017
@dtolnay
Copy link
Member Author

dtolnay commented Dec 3, 2017

This is a somewhat unconventional use case for Serde but the following gives a &'static [&'static str] slice of the field names.

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

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

#[derive(Deserialize)]
struct AuthError {
    error: String,
    description: String
}

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

    impl<'de, 'a> Deserializer<'de> for StructFieldsDeserializer<'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 fields"))
        }

        fn deserialize_struct<V>(
            self,
            _name: &'static str,
            fields: &'static [&'static str],
            visitor: V
        ) -> Result<V::Value, Self::Error>
            where V: Visitor<'de>
        {
            *self.fields = Some(fields);
            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 enum identifier ignored_any
        }
    }

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

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

@dtolnay dtolnay closed this as completed Dec 3, 2017
@dtolnay
Copy link
Member Author

dtolnay commented Dec 3, 2017

Practically the same code but for enum variant names:

#[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>());
}

@ghost
Copy link

ghost commented Feb 15, 2019

Seems like something that should be in serde-aux

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

No branches or pull requests

1 participant