1use std::io::ErrorKind;
4use std::path::{Component, Path, PathBuf};
5
6use sva_ast::TokenKind;
7use sva_core::{CliError, cwd};
8
9pub fn composition() -> Result<PathBuf, CliError> {
10 let dir = cwd()?;
11 match std::fs::read_dir(&dir) {
12 Ok(_) => Ok(dir),
13 Err(e) if e.kind() == ErrorKind::NotFound => Err(CliError::NotFound(format!(
14 "no composition at `{}`",
15 dir.display()
16 ))),
17 Err(e) => Err(CliError::Io(format!(
18 "could not read the composition at `{}`: {e}",
19 dir.display()
20 ))),
21 }
22}
23
24pub fn located(here: &Path, target: &str) -> Result<(PathBuf, String), CliError> {
27 let Ok(tokens) = sva_ast::tokenize(target) else {
28 return Ok((here.to_path_buf(), target.to_string()));
29 };
30 let refs: Vec<(sva_ast::ByteSpan, PathBuf)> = tokens
31 .iter()
32 .filter_map(|t| match &t.kind {
33 TokenKind::Ref(path) => Some((t.span, normal(&here.join(path)))),
34 _ => None,
35 })
36 .collect();
37 let root = match refs.iter().find(|(_, file)| !file.starts_with(here)) {
38 None => here.to_path_buf(),
39 Some((_, file)) => holding(file),
40 };
41 let mut out = String::with_capacity(target.len());
42 let mut at = 0;
43 for (span, file) in &refs {
44 let inside = file.strip_prefix(&root).map_err(|_| {
45 CliError::Usage(format!(
46 "`{target}` reads `{}` and `{}`, which no one composition holds; read one \
47 composition's nodes at a time",
48 refs[0].1.display(),
49 file.display()
50 ))
51 })?;
52 out.push_str(&target[at..span.start]);
53 out.push('@');
54 out.push_str(&inside.to_string_lossy());
55 at = span.end;
56 }
57 out.push_str(&target[at..]);
58 Ok((root, out))
59}
60
61fn normal(path: &Path) -> PathBuf {
62 let mut out = PathBuf::new();
63 for part in path.components() {
64 match part {
65 Component::CurDir => {}
66 Component::ParentDir => {
67 out.pop();
68 }
69 other => out.push(other),
70 }
71 }
72 out
73}
74
75fn holding(file: &Path) -> PathBuf {
76 let own = file.parent().unwrap_or(Path::new("/")).to_path_buf();
77 own.ancestors()
78 .find(|dir| dir.join(sva_ast::VARIABLES).is_dir())
79 .map_or(own.clone(), Path::to_path_buf)
80}