use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DenialBody {
pub error: String,
#[serde(default)]
pub action_url: Option<String>,
#[serde(default)]
pub action_label: Option<String>,
}
impl DenialBody {
pub fn action(&self) -> Option<(&str, &str)> {
match (self.action_url.as_deref(), self.action_label.as_deref()) {
(Some(url), Some(label)) => Some((url, label)),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn a_denial_with_an_action_round_trips() {
let body = DenialBody {
error: "not included".into(),
action_url: Some("https://example.test/#more".into()),
action_label: Some("See options".into()),
};
let value = serde_json::to_value(&body).unwrap();
assert_eq!(value["error"], "not included");
let back: DenialBody = serde_json::from_value(value).unwrap();
assert_eq!(back, body);
assert_eq!(
back.action(),
Some(("https://example.test/#more", "See options"))
);
}
#[test]
fn a_plain_error_body_deserialises_with_no_action() {
let body: DenialBody = serde_json::from_value(json!({
"error": "forbidden"
}))
.unwrap();
assert_eq!(body.action_url, None);
assert_eq!(body.action(), None);
}
#[test]
fn an_action_needs_both_a_url_and_a_label() {
let body: DenialBody = serde_json::from_value(json!({
"error": "nope", "action_url": "/somewhere"
}))
.unwrap();
assert_eq!(body.action(), None);
}
}