indy_utils/
qualifiable.rs1use once_cell::sync::Lazy;
2
3use regex::Regex;
4
5use super::{invalid, Validatable, ValidationError};
6
7pub(crate) static REGEX: Lazy<Regex> =
8 Lazy::new(|| Regex::new("^([a-z0-9]+):([a-z0-9]+):(.*)$").unwrap());
9
10pub fn combine(prefix: &str, method: Option<&str>, entity: &str) -> String {
12 match method {
13 Some(method) => format!("{}:{}:{}", prefix, method, entity),
14 _ => entity.to_owned(),
15 }
16}
17
18pub fn split<'a>(prefix: &str, val: &'a str) -> (Option<&'a str>, &'a str) {
20 match REGEX.captures(&val) {
21 None => (None, val),
22 Some(caps) => {
23 if caps.get(1).map(|m| m.as_str()) == Some(prefix) {
24 (
25 Some(caps.get(2).unwrap().as_str()),
26 caps.get(3).unwrap().as_str(),
27 )
28 } else {
29 (None, val)
30 }
31 }
32 }
33}
34
35pub fn is_fully_qualified(entity: &str) -> bool {
37 REGEX.captures(entity).is_some()
38}
39
40pub trait Qualifiable: From<String> + std::ops::Deref<Target = str> + Validatable {
42 fn prefix() -> &'static str;
43
44 fn combine(method: Option<&str>, entity: &str) -> Self {
45 Self::from(combine(Self::prefix(), method, entity))
46 }
47
48 fn split<'a>(&'a self) -> (Option<&'a str>, &'a str) {
49 split(Self::prefix(), self.deref())
50 }
51
52 fn get_method<'a>(&'a self) -> Option<&'a str> {
53 let (method, _rest) = self.split();
54 method
55 }
56
57 fn default_method(&self, method: Option<&str>) -> Self {
58 let (prev_method, rest) = self.split();
59 match prev_method {
60 Some(_) => Self::from(self.to_string()),
61 None => Self::combine(method, rest),
62 }
63 }
64
65 fn replace_method(&self, method: Option<&str>) -> Self {
66 let (_method, rest) = self.split();
67 Self::combine(method, rest)
68 }
69
70 fn remove_method(&self, method: &str) -> Self {
71 let (prev_method, rest) = self.split();
72 if prev_method == Some(method) {
73 Self::combine(None, rest)
74 } else {
75 Self::from(self.to_string())
76 }
77 }
78
79 fn from_str(entity: &str) -> Result<Self, ValidationError> {
80 let result = Self::from(entity.to_owned());
81 result.validate()?;
82 Ok(result)
83 }
84
85 fn is_fully_qualified(&self) -> bool {
86 self.get_method().is_some()
87 }
88
89 fn to_qualified(&self, method: &str) -> Result<Self, ValidationError> {
90 match self.split() {
91 (None, rest) => Ok(Self::combine(Some(method), rest)),
92 (Some(prev_method), rest) if prev_method == method => {
93 Ok(Self::combine(Some(method), rest))
94 }
95 _ => Err(invalid!(
96 "Identifier is already qualified with another method",
97 )),
98 }
99 }
100
101 fn to_unqualified(&self) -> Self {
102 let (_, rest) = self.split();
103 Self::from(rest.to_owned())
104 }
105}
106
107#[macro_export]
109macro_rules! qualifiable_type {
110 ($newtype:ident, $doc:expr) => {
111 $crate::serde_derive_impl! {
112 #[doc=$doc]
113 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
114 pub struct $newtype(pub String);
115 }
116
117 impl From<String> for $newtype {
118 fn from(val: String) -> Self {
119 Self(val)
120 }
121 }
122
123 impl std::ops::Deref for $newtype {
124 type Target = str;
125 fn deref(&self) -> &str {
126 &self.0
127 }
128 }
129
130 impl std::fmt::Display for $newtype {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 f.write_str(self.0.as_str())
133 }
134 }
135 };
136 ($newtype:ident) => {
137 qualifiable_type!($newtype, "");
138 };
139}