Введите строку, которая должна соответствовать некоторому синтаксису - PullRequest
3 голосов
/ 11 апреля 2019

Я хотел бы создать «ограниченный» тип, который ведет себя как String, но по построению проверяет, что он соответствует некоторому синтаксису.Например, мы можем создать тип Identifier, который действует как String, но обеспечивает соответствие всех символов [a-zA-Z_].

В частности, я хочу, чтобы были реализованы обычные черты (Display,Ord и т. Д.), И я хочу, чтобы serde::Serialize и serde::Deserialize вели себя так же, как с обычным String, за исключением того, что мы проверяем при десериализации.

Существует ли идиоматический способ сделать этоили мне придется реализовать все черты вручную?

1 Ответ

3 голосов
/ 11 апреля 2019

Идиоматическим способом было бы создать новый тип вокруг String и получить черты, которые могут быть получены.

Например:

use serde;
use serde_json;

#[derive(
    Eq, PartialEq, Ord, PartialOrd,
    Debug, Default,
    serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)] // to serialize as a string
pub struct Identifier(
    #[serde(deserialize_with = "Identifier::deserialize")]
    String
);

impl Identifier {
    pub fn new(s: String) -> Result<Identifier, &'static str> {
        // do some validation
        if !s.is_empty() && s.contains('a') {
            Ok(Identifier(s))
        } else {
            Err("Not valid")
        }
    }

    fn deserialize<'de, D>(d: D) -> Result<String, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::de::Error;
        use serde::Deserialize;

        let s = String::deserialize(d)?;
        Identifier::new(s).map(|i| i.0).map_err(D::Error::custom)
    }
}

fn main() {
    let a = Identifier::new("aaa".into()).unwrap();
    let b: Identifier = serde_json::from_str(r#""faaa""#).unwrap();

    println!("{:?}, {:?}", a, b);
    println!("{}", a == b);
}
...