J'essaie de désérialiser JSON dans une structure qui contient un champ optionnel. authorization
. Le JSON peut ou non inclure ce champ. Si c'est le cas, je procède à une désérialisation personnalisée dans un fichier hyper::header::Authorization<hyper::header::Scheme>
. Parce que Authorization
nécessite un type générique pour les Scheme
Je suis tenu (comme je l'ai écrit) d'inclure le type générique dans ma structure.
Tous les tests sont réussis, mais le dernier ( de_json_none
, celui pour JSON sans le champ d'autorisation) est sémantiquement bizarre parce que je dois cibler une variable avec une valeur définie. Scheme
(soit Bearer
comme indiqué ou Basic
), qui n'ont aucun sens pour ces données, bien qu'elles soient parfaitement valables du point de vue de Rust.
La raison en est claire, mais c'est quelque chose que je ne veux pas et que je ne sais pas comment résoudre.
Je veux écrire un gestionnaire Rocket qui ne correspond qu'aux données qui contiennent le champ d'autorisation de type Authorization<Bearer>
en fixant le type de données à Headers<Bearer>
. Pour l'instant, cela correspondrait également à des données qui n'ont pas de champ du tout. Je n'ai pas non plus de moyen clair d'appeler les données avec le champ manquant spécifiquement par type.
Je suis à la recherche de suggestions sur la manière de remanier ce code pour refléter le fait que Headers
a en réalité trois incarnations distinctes, qui s'excluent mutuellement ( Basic
, Bearer
y None
). Peut-être devrais-je chercher à faire quelque chose avec un enum ?
extern crate hyper;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
use hyper::header::{Authorization, Header, Raw, Scheme};
use serde::{Deserialize, Deserializer};
#[derive(Debug, Deserialize, PartialEq)]
struct Headers<S>
where
S: Scheme + 'static,
{
#[serde(deserialize_with = "auth_header", default = "no_auth")]
authorization: Option<Authorization<S>>,
#[serde(rename = ":path")]
path: String,
}
fn auth_header<'de, D, S>(deserializer: D) -> Result<Option<Authorization<S>>, D::Error>
where
D: Deserializer<'de>,
S: Scheme + 'static,
{
let s = String::deserialize(deserializer)?;
let auth = Authorization::parse_header(&Raw::from(s.into_bytes()));
auth.map(|a| Some(a)).map_err(serde::de::Error::custom)
}
fn no_auth<S>() -> Option<Authorization<S>>
where
S: Scheme + 'static,
{
None
}
#[cfg(test)]
mod test {
use hyper::header::{Basic, Bearer};
use serde_json;
use super::*;
#[test]
fn de_json_basic() {
let data = r#"{
"authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
":path": "/service/",
":method": "GET"
}"#;
let message = Headers {
authorization: Some(Authorization(Basic {
username: "Aladdin".to_owned(),
password: Some("open sesame".to_owned()),
})),
path: "/service/".to_owned(),
};
let h: Headers<Basic> = serde_json::from_str(data).unwrap();
assert_eq!(message, h);
}
#[test]
fn de_json_bearer() {
let data = r#"{
"authorization": "Bearer fpKL54jvWmEGVoRdCNjG",
":path": "/service/",
":method": "GET"
}"#;
let message = Headers {
authorization: Some(Authorization(
Bearer { token: "fpKL54jvWmEGVoRdCNjG".to_owned() },
)),
path: "/service/".to_owned(),
};
let h: Headers<Bearer> = serde_json::from_str(data).unwrap();
assert_eq!(message, h);
}
#[test]
fn de_json_none() {
let data = r#"{
":path": "/service/",
":method": "GET"
}"#;
let message = Headers {
authorization: None,
path: "/service/".to_owned(),
};
let h: Headers<Bearer> = serde_json::from_str(data).unwrap();
// this also works, though neither should ideally
// let h: Headers<Basic> = serde_json::from_str(data).unwrap();
assert_eq!(message, h);
}
}