Skip to main content

made_core/value_objects/ceremony/
context_key.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::DomainError;
6
7/// A top-level key in ceremony context.
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct ContextKey(String);
11
12impl ContextKey {
13    pub fn new(raw: impl Into<String>) -> Result<Self, DomainError> {
14        let value = raw.into();
15        let trimmed = value.trim();
16        if trimmed.is_empty() {
17            return Err(DomainError::EmptyField {
18                field: "context_key",
19            });
20        }
21        if trimmed.chars().any(char::is_control) {
22            return Err(DomainError::InvalidCharacters {
23                field: "context_key",
24            });
25        }
26        Ok(Self(trimmed.to_owned()))
27    }
28
29    pub fn from_role_from(raw: &str) -> Result<Self, DomainError> {
30        let Some(key) = raw.strip_prefix("context.") else {
31            return Err(DomainError::InvariantViolated {
32                reason: "dynamic role binding must use context.<key>",
33            });
34        };
35        Self::new(key)
36    }
37
38    #[must_use]
39    pub fn as_str(&self) -> &str {
40        &self.0
41    }
42
43    #[must_use]
44    pub fn role_from(&self) -> String {
45        format!("context.{}", self.0)
46    }
47}
48
49impl fmt::Display for ContextKey {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        f.write_str(&self.0)
52    }
53}