nix_compat/derived_path/
mod.rs1use std::{fmt, str::FromStr};
2
3mod legacy;
4mod output_spec;
5
6pub use legacy::LegacyDerivedPath;
7pub use output_spec::{OutputSpec, ParseOutputSpecError};
8
9use crate::store_path;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub enum DerivedPath {
21 Opaque(store_path::StorePath),
22 Built {
23 drv_path: store_path::StorePath,
24 outputs: OutputSpec,
25 },
26}
27
28impl DerivedPath {
29 pub fn into_legacy_format(self) -> LegacyDerivedPath {
30 LegacyDerivedPath::from_path(self)
31 }
32
33 pub fn as_legacy_format(&self) -> &LegacyDerivedPath {
34 unsafe { &*(self as *const Self as *const LegacyDerivedPath) }
36 }
37}
38
39impl fmt::Display for DerivedPath {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 DerivedPath::Opaque(store_path) => write!(f, "{}", store_path.to_absolute_path()),
43 DerivedPath::Built { drv_path, outputs } => {
44 write!(f, "{}^{}", drv_path.to_absolute_path(), outputs)
45 }
46 }
47 }
48}
49
50impl FromStr for DerivedPath {
51 type Err = ParseDerivedPathError;
52
53 fn from_str(s: &str) -> Result<Self, Self::Err> {
54 if let Some((prefix, outputs_s)) = s.rsplit_once('^') {
55 let drv_path = store_path::StorePath::from_absolute_path(prefix.as_bytes())?;
56 let outputs = outputs_s.parse::<OutputSpec>()?;
57 Ok(DerivedPath::Built { drv_path, outputs })
58 } else {
59 Ok(DerivedPath::Opaque(
60 store_path::StorePath::from_absolute_path(s.as_bytes())?,
61 ))
62 }
63 }
64}
65
66#[derive(thiserror::Error, Debug)]
67pub enum ParseDerivedPathError {
68 #[error("failed to parse store path")]
69 StorePath(#[from] store_path::ParseStorePathError),
70 #[error("store path does not point to a derivation")]
71 MissingDrvSuffix,
72 #[error("failed to parse output spec")]
73 OutputSpec(#[from] ParseOutputSpecError),
74}
75
76#[cfg(test)]
77mod tests {
78 use rstest::rstest;
79
80 use super::*;
81
82 #[rstest]
83 #[case("/nix/store/00000000000000000000000000000000-test.drv", DerivedPath::Opaque("00000000000000000000000000000000-test.drv".parse().unwrap()))]
84 #[case("/nix/store/00000000000000000000000000000000-test.drv^out", DerivedPath::Built {
85 drv_path: "00000000000000000000000000000000-test.drv".parse().unwrap(),
86 outputs: "out".parse().unwrap(),
87 })]
88 #[case("/nix/store/00000000000000000000000000000000-test.drv^*", DerivedPath::Built {
89 drv_path: "00000000000000000000000000000000-test.drv".parse().unwrap(),
90 outputs: "*".parse().unwrap(),
91 })]
92 #[case("/nix/store/00000000000000000000000000000000-test.drv^bin,lib", DerivedPath::Built {
93 drv_path: "00000000000000000000000000000000-test.drv".parse().unwrap(),
94 outputs: "bin,lib".parse().unwrap(),
95 })]
96 fn parse(#[case] input: &str, #[case] expected: DerivedPath) {
97 let actual = input.parse::<DerivedPath>().unwrap();
98 assert_eq!(actual, expected);
99 }
100
101 #[rstest]
102 #[case("/nix/store/00000000000000000000000000000000-test.drv^out^bin,lib")]
104 #[case("/nix/store/00000000000000000000000000000000-test.drv^out^bin^lib")]
106 #[case("/nix/store/00000000000000000000000000000000-test.drv!out")]
108 #[case("/nix/store/00000000000000000000000000000000-test.drv!out^bin")]
110 #[case("/nix/store/00000000000000000000000000000000-test.drv^out^bin!out^lib")]
112 #[should_panic(expected = "StorePath(Name)")]
113 fn parse_fail(#[case] input: &str) {
114 input.parse::<DerivedPath>().unwrap();
115 }
116
117 #[rstest]
118 #[case(DerivedPath::Opaque("00000000000000000000000000000000-test.drv".parse().unwrap()), "/nix/store/00000000000000000000000000000000-test.drv")]
119 #[case(DerivedPath::Built {
120 drv_path: "00000000000000000000000000000000-test.drv".parse().unwrap(),
121 outputs: "out".parse().unwrap(),
122 }, "/nix/store/00000000000000000000000000000000-test.drv^out")]
123 #[case(DerivedPath::Built {
124 drv_path: "00000000000000000000000000000000-test.drv".parse().unwrap(),
125 outputs: "*".parse().unwrap(),
126 }, "/nix/store/00000000000000000000000000000000-test.drv^*")]
127 #[case(DerivedPath::Built {
128 drv_path: "00000000000000000000000000000000-test.drv".parse().unwrap(),
129 outputs: "bin,lib".parse().unwrap(),
130 }, "/nix/store/00000000000000000000000000000000-test.drv^bin,lib")]
131 fn display(#[case] value: DerivedPath, #[case] expected: &str) {
132 assert_eq!(value.to_string(), expected);
133 }
134}