1use crate::prelude::{
16 fmt::{Display, Error as FmtError, Formatter},
17 iter,
18 vec::Vec,
19};
20
21use crate::{
22 form::{Form, MetaForm, PortableForm},
23 utils::is_rust_identifier,
24 IntoPortable, Registry,
25};
26use scale::Encode;
27#[cfg(feature = "serde")]
28use serde::{de::DeserializeOwned, Deserialize, Serialize};
29
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
38#[cfg_attr(
39 feature = "serde",
40 serde(bound(
41 serialize = "T::Type: Serialize, T::String: Serialize",
42 deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned",
43 ))
44)]
45#[cfg_attr(feature = "serde", serde(transparent))]
46#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
47#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
48#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Encode)]
49pub struct Path<T: Form = MetaForm> {
50 pub segments: Vec<T::String>,
52}
53
54impl<T> Default for Path<T>
55where
56 T: Form,
57{
58 fn default() -> Self {
59 Path {
60 segments: Vec::new(),
61 }
62 }
63}
64
65impl IntoPortable for Path {
66 type Output = Path<PortableForm>;
67
68 fn into_portable(self, _registry: &mut Registry) -> Self::Output {
69 Path {
70 segments: self.segments.into_iter().map(Into::into).collect(),
71 }
72 }
73}
74
75impl Display for Path<PortableForm> {
76 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
77 write!(f, "{}", self.segments.join("::"))
78 }
79}
80
81impl Path<MetaForm> {
82 pub fn new(ident: &'static str, module_path: &'static str) -> Path {
88 let segments = module_path.split("::");
89 Self::from_segments(segments.chain(iter::once(ident)))
90 .expect("All path segments should be valid Rust identifiers")
91 }
92
93 pub fn new_with_replace(
104 ident: &'static str,
105 module_path: &'static str,
106 segment_replace: &[(&'static str, &'static str)],
107 ) -> Path {
108 let segments = module_path.split("::");
109 Self::from_segments(
110 segments
111 .chain(iter::once(ident))
112 .map(|s| segment_replace.iter().find(|r| s == r.0).map_or(s, |r| r.1)),
113 )
114 .expect("All path segments should be valid Rust identifiers")
115 }
116
117 pub fn from_segments<I>(segments: I) -> Result<Self, PathError>
124 where
125 I: IntoIterator<Item = <MetaForm as Form>::String>,
126 {
127 let segments = segments.into_iter().collect::<Vec<_>>();
128 if segments.is_empty() {
129 return Err(PathError::MissingSegments);
130 }
131 if let Some(err_at) = segments.iter().position(|seg| !is_rust_identifier(seg)) {
132 return Err(PathError::InvalidIdentifier { segment: err_at });
133 }
134 Ok(Path { segments })
135 }
136
137 pub(crate) fn prelude(ident: <MetaForm as Form>::String) -> Self {
143 Self::from_segments([ident])
144 .unwrap_or_else(|_| panic!("{ident:?} is not a valid Rust identifier"))
145 }
146}
147
148impl<T> Path<T>
149where
150 T: Form,
151{
152 #[allow(unused)]
154 pub(crate) fn voldemort() -> Self {
155 Self {
156 segments: Vec::new(),
157 }
158 }
159
160 pub fn from_segments_unchecked<I>(segments: I) -> Path<T>
164 where
165 I: IntoIterator<Item = T::String>,
166 {
167 Self {
168 segments: segments.into_iter().collect(),
169 }
170 }
171
172 #[deprecated(
174 since = "2.5.0",
175 note = "Prefer to access the fields directly; this getter will be removed in the next major version"
176 )]
177 pub fn segments(&self) -> &[T::String] {
178 &self.segments
179 }
180
181 pub fn is_empty(&self) -> bool {
183 self.segments.is_empty()
184 }
185
186 pub fn ident(&self) -> Option<T::String> {
188 self.segments.iter().last().cloned()
189 }
190
191 pub fn namespace(&self) -> &[T::String] {
193 self.segments.split_last().map(|(_, ns)| ns).unwrap_or(&[])
194 }
195}
196
197#[derive(PartialEq, Eq, Debug)]
199pub enum PathError {
200 MissingSegments,
202 InvalidIdentifier {
204 segment: usize,
206 },
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212
213 #[test]
214 fn path_ok() {
215 assert_eq!(
216 Path::from_segments(vec!["hello"]),
217 Ok(Path {
218 segments: vec!["hello"]
219 })
220 );
221 assert_eq!(
222 Path::from_segments(vec!["Hello", "World"]),
223 Ok(Path {
224 segments: vec!["Hello", "World"]
225 })
226 );
227 assert_eq!(
228 Path::from_segments(vec!["_"]),
229 Ok(Path {
230 segments: vec!["_"]
231 })
232 );
233 }
234
235 #[test]
236 fn path_with_raw_identifers_ok() {
237 assert_eq!(
238 Path::from_segments(vec!["r#mod", "r#Struct"]),
239 Ok(Path {
240 segments: vec!["r#mod", "r#Struct"]
241 })
242 );
243 }
244
245 #[test]
246 fn path_err() {
247 assert_eq!(
248 Path::from_segments(Vec::new()),
249 Err(PathError::MissingSegments)
250 );
251 assert_eq!(
252 Path::from_segments(vec![""]),
253 Err(PathError::InvalidIdentifier { segment: 0 })
254 );
255 assert_eq!(
256 Path::from_segments(vec!["1"]),
257 Err(PathError::InvalidIdentifier { segment: 0 })
258 );
259 assert_eq!(
260 Path::from_segments(vec!["Hello", ", World!"]),
261 Err(PathError::InvalidIdentifier { segment: 1 })
262 );
263 }
264
265 #[test]
266 fn path_from_module_path_and_ident() {
267 assert_eq!(
268 Path::new("Planet", "hello::world"),
269 Path {
270 segments: vec!["hello", "world", "Planet"]
271 }
272 );
273 assert_eq!(
274 Path::from_segments(vec!["Earth", "::world"]),
275 Err(PathError::InvalidIdentifier { segment: 1 })
276 );
277 }
278
279 #[test]
280 fn path_get_namespace_and_ident() {
281 let path = Path::new("Planet", "hello::world");
282 assert_eq!(path.namespace(), &["hello", "world"]);
283 assert_eq!(path.ident(), Some("Planet"));
284 }
285
286 #[test]
287 #[should_panic]
288 fn path_new_panics_with_invalid_identifiers() {
289 Path::new("Planet", "hello$!@$::world");
290 }
291
292 #[test]
293 fn path_display() {
294 let path = Path::new("Planet", "hello::world").into_portable(&mut Default::default());
295 assert_eq!("hello::world::Planet", format!("{}", path))
296 }
297}