cloud_sdk/operation/
operation_id.rs1use core::fmt;
4
5pub const MAX_OPERATION_ID_BYTES: usize = 128;
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum OperationIdError {
11 Empty,
13 TooLong,
15 InvalidByte,
17}
18
19impl fmt::Display for OperationIdError {
20 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
21 formatter.write_str(match self {
22 Self::Empty => "operation identifier is empty",
23 Self::TooLong => "operation identifier is too long",
24 Self::InvalidByte => "operation identifier contains an invalid byte",
25 })
26 }
27}
28
29impl core::error::Error for OperationIdError {}
30
31#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
33pub struct OperationId(&'static str);
34
35impl OperationId {
36 pub fn new(value: &'static str) -> Result<Self, OperationIdError> {
38 if value.is_empty() {
39 return Err(OperationIdError::Empty);
40 }
41 if value.len() > MAX_OPERATION_ID_BYTES {
42 return Err(OperationIdError::TooLong);
43 }
44 if value
45 .bytes()
46 .any(|byte| !byte.is_ascii_lowercase() && !byte.is_ascii_digit() && byte != b'_')
47 {
48 return Err(OperationIdError::InvalidByte);
49 }
50 Ok(Self(value))
51 }
52
53 #[must_use]
55 pub const fn as_str(self) -> &'static str {
56 self.0
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::{OperationId, OperationIdError};
63
64 #[test]
65 fn accepts_source_style_identifiers_and_rejects_ambiguous_text() {
66 assert_eq!(
67 OperationId::new("get_server").map(OperationId::as_str),
68 Ok("get_server")
69 );
70 assert_eq!(OperationId::new(""), Err(OperationIdError::Empty));
71 assert_eq!(
72 OperationId::new("Get-Server"),
73 Err(OperationIdError::InvalidByte)
74 );
75 }
76}