nickel_lang_package/index/
path.rs1use std::path::{Path, PathBuf};
4
5use nickel_lang_core::cache::normalize_rel_path;
6
7#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, Default)]
12#[serde(transparent)]
13pub struct RelativePath {
14 inner: String,
15}
16
17impl std::fmt::Display for RelativePath {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 self.inner.fmt(f)
20 }
21}
22
23impl RelativePath {
24 pub fn is_empty(&self) -> bool {
26 self.inner.is_empty()
27 }
28
29 pub fn components(&self) -> impl Iterator<Item = &str> + '_ {
31 self.as_ref().components().map(|c| {
32 match c {
33 std::path::Component::Normal(os_str) => os_str.to_str().unwrap(),
35 _ => unreachable!(),
37 }
38 })
39 }
40}
41
42impl<'de> serde::Deserialize<'de> for RelativePath {
43 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
44 where
45 D: serde::Deserializer<'de>,
46 {
47 let path = PathBuf::deserialize(deserializer)?;
48 path.try_into().map_err(serde::de::Error::custom)
49 }
50}
51
52impl AsRef<Path> for RelativePath {
53 fn as_ref(&self) -> &Path {
54 self.inner.as_ref()
55 }
56}
57
58#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, thiserror::Error)]
59pub enum RelativePathErrorKind {
60 #[error("absolute path")]
62 Absolute,
63 #[error("points outside its parent")]
65 OutOfBounds,
66 #[error("invalid UTF-8")]
68 InvalidUtf8,
69}
70
71#[derive(Clone, Debug, PartialEq, Eq, Hash, thiserror::Error)]
72#[error("could not convert {path} to a relative path: {kind}")]
73pub struct RelativePathError {
74 pub path: PathBuf,
76 pub kind: RelativePathErrorKind,
78}
79
80impl TryFrom<PathBuf> for RelativePath {
81 type Error = RelativePathError;
82
83 fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
84 if path.is_absolute() {
85 Err(RelativePathError {
86 path,
87 kind: RelativePathErrorKind::Absolute,
88 })
89 } else {
90 let normalized = normalize_rel_path(&path);
91 if normalized.components().next() == Some(std::path::Component::ParentDir) {
92 Err(RelativePathError {
93 path,
94 kind: RelativePathErrorKind::OutOfBounds,
95 })
96 } else {
97 let inner =
98 normalized
99 .into_os_string()
100 .into_string()
101 .map_err(|s| RelativePathError {
102 path: s.into(),
103 kind: RelativePathErrorKind::InvalidUtf8,
104 })?;
105 Ok(RelativePath { inner })
106 }
107 }
108 }
109}