use crate::{
form::{CompactForm, Form, MetaForm},
tm_std::*,
utils::is_rust_identifier,
IntoCompact, Registry,
};
use scale::{Decode, Encode};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, Encode, Decode)]
#[serde(transparent)]
#[serde(bound(
serialize = "T::Type: Serialize, T::String: Serialize",
deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned"
))]
pub struct Path<T: Form = MetaForm> {
segments: Vec<T::String>,
}
impl<T> Default for Path<T>
where
T: Form,
{
fn default() -> Self {
Path { segments: Vec::new() }
}
}
impl IntoCompact for Path {
type Output = Path<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
Path {
segments: registry.map_into_compact(self.segments),
}
}
}
impl Path {
pub fn new(ident: &'static str, module_path: &'static str) -> Path {
let mut segments = module_path.split("::").collect::<Vec<_>>();
segments.push(ident);
Self::from_segments(segments).expect("All path segments should be valid Rust identifiers")
}
#[allow(unused)]
pub(crate) fn voldemort() -> Path {
Path { segments: Vec::new() }
}
pub(crate) fn prelude(ident: &'static str) -> Path {
Self::from_segments(vec![ident]).unwrap_or_else(|_| panic!("{} is not a valid Rust identifier", ident))
}
pub fn from_segments<I>(segments: I) -> Result<Path, PathError>
where
I: IntoIterator<Item = &'static str>,
{
let segments = segments.into_iter().collect::<Vec<_>>();
if segments.is_empty() {
return Err(PathError::MissingSegments);
}
if let Some(err_at) = segments.iter().position(|seg| !is_rust_identifier(seg)) {
return Err(PathError::InvalidIdentifier { segment: err_at });
}
Ok(Path { segments })
}
}
impl<T> Path<T>
where
T: Form,
{
pub fn segments(&self) -> &[T::String] {
&self.segments
}
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
pub fn ident(&self) -> Option<T::String> {
self.segments.iter().last().cloned()
}
pub fn namespace(&self) -> &[T::String] {
self.segments.split_last().map(|(_, ns)| ns).unwrap_or(&[])
}
}
#[derive(PartialEq, Eq, Debug)]
pub enum PathError {
MissingSegments,
InvalidIdentifier {
segment: usize,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn path_ok() {
assert_eq!(
Path::from_segments(vec!["hello"]),
Ok(Path {
segments: vec!["hello"]
})
);
assert_eq!(
Path::from_segments(vec!["Hello", "World"]),
Ok(Path {
segments: vec!["Hello", "World"]
})
);
assert_eq!(Path::from_segments(vec!["_"]), Ok(Path { segments: vec!["_"] }));
}
#[test]
fn path_err() {
assert_eq!(Path::from_segments(vec![]), Err(PathError::MissingSegments));
assert_eq!(
Path::from_segments(vec![""]),
Err(PathError::InvalidIdentifier { segment: 0 })
);
assert_eq!(
Path::from_segments(vec!["1"]),
Err(PathError::InvalidIdentifier { segment: 0 })
);
assert_eq!(
Path::from_segments(vec!["Hello", ", World!"]),
Err(PathError::InvalidIdentifier { segment: 1 })
);
}
#[test]
fn path_from_module_path_and_ident() {
assert_eq!(
Path::new("Planet", "hello::world"),
Path {
segments: vec!["hello", "world", "Planet"]
}
);
assert_eq!(
Path::from_segments(vec!["Earth", "::world"]),
Err(PathError::InvalidIdentifier { segment: 1 })
);
}
#[test]
fn path_get_namespace_and_ident() {
let path = Path::new("Planet", "hello::world");
assert_eq!(path.namespace(), &["hello", "world"]);
assert_eq!(path.ident(), Some("Planet"));
}
#[test]
#[should_panic]
fn path_new_panics_with_invalid_identifiers() {
Path::new("Planet", "hello$!@$::world");
}
}