1use std::path::PathBuf;
2
3use logos::Logos;
4use spade_common::name::Path as SpadePath;
5use spade_parser::{lexer, Parser};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct NamespacedFile {
9 pub base_namespace: SpadePath,
12 pub namespace: SpadePath,
14 pub file: PathBuf,
15}
16
17pub fn namespaced_file(arg: &str) -> Result<NamespacedFile, String> {
20 let parts = arg.split(',').collect::<Vec<_>>();
21
22 match parts.len() {
23 0 => Err("Expected a string".to_string()),
24 1 => Ok(NamespacedFile {
25 file: arg.into(),
26 namespace: SpadePath(vec![]),
27 base_namespace: SpadePath(vec![]),
28 }),
29 3 => {
30 let root_namespace = if parts[0].is_empty() {
31 SpadePath(vec![])
32 } else {
33 let mut root_parser = Parser::new(lexer::TokenKind::lexer(parts[0]), 0);
34 root_parser
35 .path()
36 .map_err(|e| {
37 format!(
38 "\nwhen parsing '{}': {}",
39 parts[0],
40 e.labels.message.as_str()
41 )
42 })?
43 .inner
44 };
45
46 let namespace = if parts[1].is_empty() {
47 SpadePath(vec![])
48 } else {
49 let mut namespace_parser = Parser::new(lexer::TokenKind::lexer(parts[1]), 0);
51 namespace_parser
52 .path()
53 .map_err(|e| {
54 format!(
55 "\nwhen parsing '{}': {}",
56 parts[1],
57 e.labels.message.as_str()
58 )
59 })?
60 .inner
61 };
62
63 Ok(NamespacedFile {
64 base_namespace: root_namespace,
65 file: parts[2].into(),
66 namespace,
67 })
68 }
69 other => Err(format!(
70 "Expected filename or <root>,<namespace>,<filename>. Got string with {other} commas"
71 )),
72 }
73}
74
75pub fn dummy_file() -> NamespacedFile {
76 namespaced_file("a,a,a.spade").unwrap()
77}
78
79#[cfg(test)]
80mod tests {
81 use spade_common::{
82 location_info::WithLocation as _,
83 name::{Identifier, Path as SpadePath, PathSegment},
84 };
85
86 use crate::namespaced_file::NamespacedFile;
87
88 use super::namespaced_file;
89
90 #[test]
91 fn parsing_namespaced_file_works() {
92 assert_eq!(
93 namespaced_file("a,a::b,b.spade"),
94 Ok(NamespacedFile {
95 base_namespace: SpadePath(vec![PathSegment::Named(
96 Identifier::intern("a").nowhere()
97 )]),
98 namespace: SpadePath(vec![
99 PathSegment::Named(Identifier::intern("a").nowhere()),
100 PathSegment::Named(Identifier::intern("b").nowhere())
101 ]),
102 file: "b.spade".into(),
103 })
104 );
105 }
106
107 #[test]
108 fn invalid_path_errors_without_panic() {
109 assert!(namespaced_file("lib,lib::pipeline,pipeline.spade").is_err());
110 }
111}