1use serde::{Deserialize, Serialize};
2
3use crate::GateValidationError;
4
5#[derive(Clone, Debug, Serialize)]
10#[serde(tag = "kind", rename_all = "snake_case")]
11pub enum Obligation {
12 Audit {
13 tag: String,
14 },
15 RateLimit {
16 window_secs: u64,
17 max: u32,
18 },
19 Custom {
21 value: serde_json::Value,
22 },
23}
24
25#[derive(Deserialize)]
27#[serde(tag = "kind", rename_all = "snake_case")]
28enum RawObligation {
29 Audit { tag: String },
30 RateLimit { window_secs: u64, max: u32 },
31 Custom { value: serde_json::Value },
32}
33
34impl TryFrom<RawObligation> for Obligation {
35 type Error = GateValidationError;
36
37 fn try_from(raw: RawObligation) -> Result<Self, Self::Error> {
38 match raw {
39 RawObligation::Audit { tag } => {
40 if tag.is_empty() {
41 return Err(GateValidationError::EmptyAuditTag);
42 }
43 Ok(Obligation::Audit { tag })
44 }
45 RawObligation::RateLimit { window_secs, max } => {
46 Obligation::try_rate_limit(window_secs, max)
47 }
48 RawObligation::Custom { value } => Ok(Obligation::Custom { value }),
49 }
50 }
51}
52
53impl<'de> Deserialize<'de> for Obligation {
54 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55 where
56 D: serde::Deserializer<'de>,
57 {
58 let raw = RawObligation::deserialize(deserializer)?;
59 Obligation::try_from(raw).map_err(serde::de::Error::custom)
60 }
61}
62
63impl Obligation {
64 pub fn try_rate_limit(window_secs: u64, max: u32) -> Result<Self, GateValidationError> {
67 if window_secs == 0 {
68 return Err(GateValidationError::ZeroRateLimitWindow);
69 }
70 if max == 0 {
71 return Err(GateValidationError::ZeroRateLimitMax);
72 }
73 Ok(Self::RateLimit { window_secs, max })
74 }
75
76 pub fn rate_limit(window_secs: u64, max: u32) -> Self {
78 Self::try_rate_limit(window_secs, max)
79 .expect("Obligation::rate_limit: window_secs and max must be > 0")
80 }
81}