nix_compat/store_path/
borrowed.rs1use std::fmt;
2
3use crate::{
4 nixbase32,
5 store_path::{
6 DIGEST_SIZE, ENCODED_DIGEST_SIZE, ParseStorePathError, STORE_DIR_WITH_SLASH, StorePath,
7 StorePathName,
8 },
9};
10
11#[derive(Clone, Debug, Eq, PartialEq, Hash)]
13pub struct StorePathRef<'a> {
14 digest: [u8; DIGEST_SIZE],
15 name: &'a StorePathName,
16}
17
18impl<'a> StorePathRef<'a> {
19 pub fn digest(&self) -> &[u8; DIGEST_SIZE] {
20 &self.digest
21 }
22
23 pub fn name(&self) -> &'a StorePathName {
24 self.name
25 }
26
27 pub fn to_owned(&self) -> StorePath {
28 StorePath {
29 digest: self.digest,
30 name: self.name.to_owned(),
31 }
32 }
33
34 pub fn from_bytes(s: &'a [u8]) -> Result<Self, ParseStorePathError> {
37 if s.len() < ENCODED_DIGEST_SIZE + 2 {
43 Err(ParseStorePathError::Length)?
44 }
45
46 let digest = nixbase32::decode_fixed(&s[..ENCODED_DIGEST_SIZE])?;
47
48 if s[ENCODED_DIGEST_SIZE] != b'-' {
49 return Err(ParseStorePathError::MissingDash);
50 }
51
52 Ok(Self {
53 digest,
54 name: StorePathName::from_slice(&s[ENCODED_DIGEST_SIZE + 1..])?,
55 })
56 }
57
58 pub fn from_name_and_digest(
61 name: &'a StorePathName,
62 digest: &[u8],
63 ) -> Result<Self, ParseStorePathError> {
64 let digest_fixed = digest.try_into().map_err(|_| ParseStorePathError::Length)?;
65 Ok(Self::from_name_and_digest_fixed(name, digest_fixed))
66 }
67
68 pub fn from_name_and_digest_fixed(name: &'a StorePathName, digest: [u8; DIGEST_SIZE]) -> Self {
71 Self { name, digest }
72 }
73
74 pub fn from_absolute_path(s: &'a [u8]) -> Result<Self, ParseStorePathError> {
78 match s.strip_prefix(STORE_DIR_WITH_SLASH.as_bytes()) {
79 Some(s_stripped) => Self::from_bytes(s_stripped),
80 None => Err(ParseStorePathError::MissingStoreDir),
81 }
82 }
83
84 pub fn from_absolute_path_full<'p, P>(
87 path: &'p P,
88 ) -> Result<(Self, &'p std::path::Path), ParseStorePathError>
89 where
90 P: AsRef<std::path::Path> + 'p + ?Sized,
91 'p: 'a,
92 {
93 let p = path
95 .as_ref()
96 .strip_prefix(STORE_DIR_WITH_SLASH)
97 .map_err(|_| ParseStorePathError::MissingStoreDir)?;
98
99 let mut components = p.components();
100
101 use bstr::ByteSlice;
102 let first_component = <[u8]>::from_os_str(
103 components
104 .next()
105 .ok_or(ParseStorePathError::Length)?
106 .as_os_str(),
107 )
108 .ok_or(ParseStorePathError::Name)?;
109
110 if first_component.len() < 34 {
112 return Err(ParseStorePathError::Length);
113 }
114
115 let store_path = Self::from_bytes(first_component)?;
116
117 Ok((store_path, components.as_path()))
118 }
119
120 pub fn to_absolute_path(&self) -> String {
122 self.as_absolute_path_fmt().to_string()
123 }
124
125 pub fn as_absolute_path_fmt(&'a self) -> impl std::fmt::Display + 'a {
127 struct WithAbsolutePath<'a>(&'a StorePathRef<'a>);
128
129 impl std::fmt::Display for WithAbsolutePath<'_> {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 write!(f, "{STORE_DIR_WITH_SLASH}{}", self.0)
132 }
133 }
134 WithAbsolutePath(self)
135 }
136}
137
138impl fmt::Display for StorePathRef<'_> {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 write!(f, "{}-{}", nixbase32::encode(&self.digest), self.name)
144 }
145}
146
147impl PartialOrd for StorePathRef<'_> {
148 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
149 Some(self.cmp(other))
150 }
151}
152
153impl<'a> From<StorePathRef<'a>> for StorePath {
154 fn from(value: StorePathRef<'a>) -> Self {
155 Self {
156 digest: value.digest,
157 name: value.name.to_owned(),
158 }
159 }
160}
161
162impl StorePath {
163 pub fn as_ref(&self) -> StorePathRef<'_> {
164 StorePathRef {
165 digest: self.digest,
166 name: &self.name,
167 }
168 }
169}
170
171impl Ord for StorePathRef<'_> {
174 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
175 self.digest.iter().rev().cmp(other.digest.iter().rev())
176 }
177}
178
179#[cfg(feature = "hashbrown")]
182impl hashbrown::Equivalent<StorePath> for StorePathRef<'_> {
183 fn equivalent(&self, key: &StorePath) -> bool {
184 self.digest() == key.digest() && self.name() == key.name()
185 }
186}
187
188#[cfg(feature = "serde")]
189impl<'a, 'de: 'a> serde::Deserialize<'de> for StorePathRef<'a> {
190 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
191 where
192 D: serde::Deserializer<'de>,
193 {
194 let string: &'de str = serde::Deserialize::deserialize(deserializer)?;
195 let stripped: Option<&str> = string.strip_prefix(STORE_DIR_WITH_SLASH);
196 let stripped: &str = stripped.ok_or_else(|| {
197 serde::de::Error::invalid_value(
198 serde::de::Unexpected::Str(string),
199 &"store path prefix",
200 )
201 })?;
202 StorePathRef::from_bytes(stripped.as_bytes()).map_err(|_| {
203 serde::de::Error::invalid_value(serde::de::Unexpected::Str(string), &"StorePath")
204 })
205 }
206}
207
208#[cfg(feature = "serde")]
209impl serde::Serialize for StorePathRef<'_> {
210 fn serialize<SR>(&self, serializer: SR) -> Result<SR::Ok, SR::Error>
211 where
212 SR: serde::Serializer,
213 {
214 let string: String = self.to_absolute_path();
215 string.serialize(serializer)
216 }
217}