1use crate::error::{Error, Result};
16
17pub struct StageLocation {
18 pub name: String,
19 pub path: String,
20}
21
22impl std::fmt::Display for StageLocation {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 write!(f, "@{}/{}", self.name, self.path)
25 }
26}
27
28impl TryFrom<&str> for StageLocation {
29 type Error = Error;
30 fn try_from(s: &str) -> Result<Self> {
31 if !s.starts_with('@') {
32 return Err(Error::BadArgument(format!("Invalid stage location: {s}")));
33 }
34 let mut parts = s.splitn(2, '/');
35 let name = parts
36 .next()
37 .ok_or_else(|| Error::BadArgument(format!("Invalid stage location: {s}")))?
38 .trim_start_matches('@');
39 let path = parts.next().unwrap_or_default();
40 Ok(Self {
41 name: name.to_string(),
42 path: path.to_string(),
43 })
44 }
45}
46
47impl StageLocation {
48 pub fn file_path(&self, file_name: &str) -> String {
49 if self.path.ends_with('/') {
50 format!("{self}{file_name}")
51 } else {
52 format!("{self}/{file_name}")
53 }
54 }
55}
56
57#[cfg(test)]
58mod test {
59 use super::*;
60
61 #[test]
62 fn parse_stage() -> Result<()> {
63 let location = "@stage_name/path/to/file";
64 let stage = StageLocation::try_from(location)?;
65 assert_eq!(stage.name, "stage_name");
66 assert_eq!(stage.path, "path/to/file");
67 Ok(())
68 }
69
70 #[test]
71 fn parse_stage_empty_path() -> Result<()> {
72 let location = "@stage_name";
73 let stage = StageLocation::try_from(location)?;
74 assert_eq!(stage.name, "stage_name");
75 assert_eq!(stage.path, "");
76 Ok(())
77 }
78
79 #[test]
80 fn parse_stage_fail() -> Result<()> {
81 let location = "stage_name/path/to/file";
82 let stage = StageLocation::try_from(location);
83 assert!(stage.is_err());
84 Ok(())
85 }
86}