Skip to main content

miden_protocol/account/interface/
name.rs

1use alloc::borrow::Cow;
2use alloc::string::String;
3use core::fmt::Display;
4use core::str::FromStr;
5
6use crate::account::name_validation::{self, NameValidationError};
7use crate::errors::AccountComponentNameError;
8
9/// The canonical name of an account component.
10///
11/// Names follow the same syntax as [`StorageSlotName`](crate::account::StorageSlotName):
12/// `::`-separated components consisting of ASCII alphanumeric characters or underscores, with at
13/// least two components and no leading underscore on any component. See the type-level docs of
14/// [`StorageSlotName`](crate::account::StorageSlotName) for the full grammar.
15///
16/// The name is stored as a `Cow<'static, str>` so that hard-coded component names (e.g. the
17/// `NAME` constant on each standard component) can be turned into typed names via
18/// [`AccountComponentName::from_static_str`] without any runtime allocation, while runtime names
19/// (parsed strings, etc.) can still be constructed via [`AccountComponentName::new`].
20#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
21pub struct AccountComponentName(Cow<'static, str>);
22
23impl AccountComponentName {
24    // CONSTRUCTORS
25    // --------------------------------------------------------------------------------------------
26
27    /// Constructs an [`AccountComponentName`] from a `&'static str` at compile time.
28    ///
29    /// # Panics
30    ///
31    /// Panics if `name` does not satisfy the validity rules described in the type-level
32    /// documentation of [`AccountComponentName`].
33    pub const fn from_static_str(name: &'static str) -> Self {
34        match name_validation::validate(name) {
35            Ok(()) => Self(Cow::Borrowed(name)),
36            Err(_) => panic!("invalid AccountComponentName: see the type-level documentation"),
37        }
38    }
39
40    /// Constructs an [`AccountComponentName`] from any value convertible into a
41    /// `Cow<'static, str>`.
42    ///
43    /// # Errors
44    ///
45    /// Returns an error if `name` is not a valid component name (see the type-level docs).
46    pub fn new(name: impl Into<Cow<'static, str>>) -> Result<Self, AccountComponentNameError> {
47        let name = name.into();
48        name_validation::validate(&name).map_err(AccountComponentNameError::from_internal)?;
49        Ok(Self(name))
50    }
51
52    // ACCESSORS
53    // --------------------------------------------------------------------------------------------
54
55    /// Returns the component name as a string slice.
56    pub fn as_str(&self) -> &str {
57        &self.0
58    }
59}
60
61impl AccountComponentNameError {
62    /// Maps each [`NameValidationError`] variant to its corresponding
63    /// [`AccountComponentNameError`] variant.
64    pub(crate) fn from_internal(error: NameValidationError) -> Self {
65        match error {
66            NameValidationError::TooShort => Self::TooShort,
67            NameValidationError::TooLong => Self::TooLong,
68            NameValidationError::UnexpectedColon => Self::UnexpectedColon,
69            NameValidationError::UnexpectedUnderscore => Self::UnexpectedUnderscore,
70            NameValidationError::InvalidCharacter => Self::InvalidCharacter,
71        }
72    }
73}
74
75impl Display for AccountComponentName {
76    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77        f.write_str(self.as_str())
78    }
79}
80
81impl FromStr for AccountComponentName {
82    type Err = AccountComponentNameError;
83
84    fn from_str(string: &str) -> Result<Self, Self::Err> {
85        Self::new(String::from(string))
86    }
87}
88
89impl TryFrom<&str> for AccountComponentName {
90    type Error = AccountComponentNameError;
91
92    fn try_from(value: &str) -> Result<Self, Self::Error> {
93        value.parse()
94    }
95}
96
97impl TryFrom<String> for AccountComponentName {
98    type Error = AccountComponentNameError;
99
100    fn try_from(value: String) -> Result<Self, Self::Error> {
101        Self::new(value)
102    }
103}
104
105impl From<AccountComponentName> for String {
106    fn from(name: AccountComponentName) -> Self {
107        name.0.into_owned()
108    }
109}
110
111// TESTS
112// ================================================================================================
113
114#[cfg(test)]
115mod tests {
116    //! Note: Most tests live in crate::account::name_validation.
117
118    use super::*;
119
120    #[test]
121    #[should_panic(expected = "invalid AccountComponentName")]
122    fn from_static_str_panics_on_invalid_input() {
123        AccountComponentName::from_static_str("not_two_components");
124    }
125}