Skip to main content

ahri_tre_protocol/
mutation.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
4#[serde(try_from = "String", into = "String")]
5pub struct IdempotencyKey(String);
6
7impl IdempotencyKey {
8    pub fn new(value: impl Into<String>) -> Result<Self, String> {
9        let value = value.into();
10        let valid = !value.is_empty()
11            && value.len() <= 128
12            && value.bytes().all(|byte| {
13                byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'~' | b'-')
14            });
15        if valid {
16            Ok(Self(value))
17        } else {
18            Err("idempotency key must match [A-Za-z0-9._~-]{1,128}".to_string())
19        }
20    }
21
22    pub fn as_str(&self) -> &str {
23        &self.0
24    }
25}
26
27impl TryFrom<String> for IdempotencyKey {
28    type Error = String;
29
30    fn try_from(value: String) -> Result<Self, Self::Error> {
31        Self::new(value)
32    }
33}
34
35impl From<IdempotencyKey> for String {
36    fn from(value: IdempotencyKey) -> Self {
37        value.0
38    }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
42pub struct MutationOptions {
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub idempotency_key: Option<IdempotencyKey>,
45    #[serde(default, skip_serializing_if = "Vec::is_empty")]
46    pub preconditions: Vec<Precondition>,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(tag = "kind", rename_all = "snake_case")]
51pub enum Precondition {
52    ResourceVersion { target: String, version: String },
53    Exists { target: String },
54    DoesNotExist { target: String },
55    SessionState { target: String, state: String },
56    GovernanceState { target: String, state: String },
57}
58
59impl MutationOptions {
60    pub fn is_empty(&self) -> bool {
61        self.idempotency_key.is_none() && self.preconditions.is_empty()
62    }
63}