1use std::path::Path;
4
5use sva_ast::{Expr, Graph, Literal};
6use sva_core::{CliError, PROBE, define_probe_for, prepared, refuse_unresolved_bars};
7use sva_engine::{EngineError, Traced};
8
9use crate::lint::{entry_points, referenced};
10
11pub struct Traceable {
12 pub bpm: Option<f64>,
13 pub meter: Option<String>,
14 pub traced: Traced,
15}
16
17pub fn trace(dir: &Path, target: &str) -> Result<Traceable, CliError> {
19 let mut graph = prepared(&sva_ast::Dir::at(dir))?;
20 refuse_unresolved_bars(&graph)?;
21
22 let mut roots = entry_points(&graph);
23 if graph.defines(target)
24 && !roots.iter().any(|r| r == target)
25 && !referenced(&graph).contains(target)
26 {
27 roots.push(target.to_string());
28 }
29 let traced = match sva_engine::trace(&graph, &roots, target) {
30 Err(EngineError::UnknownNode(ref name)) if name == target && !graph.defines(target) => {
31 probe(&mut graph, target)?
32 }
33 other => other.map_err(CliError::Engine)?,
34 };
35 Ok(Traceable {
36 bpm: number(graph.global("bpm")),
37 meter: text(graph.global("meter")),
38 traced,
39 })
40}
41
42fn probe(graph: &mut Graph, text: &str) -> Result<Traced, CliError> {
44 define_probe_for(graph, text)?;
45 let roots = entry_points(graph);
46 sva_engine::trace(graph, &roots, PROBE).map_err(CliError::Engine)
47}
48
49fn number(e: Option<&Expr>) -> Option<f64> {
50 match e {
51 Some(Expr::Lit(Literal::Num(v))) => Some(*v),
52 _ => None,
53 }
54}
55
56fn text(e: Option<&Expr>) -> Option<String> {
57 match e {
58 Some(Expr::Lit(Literal::Str(v))) => Some(v.clone()),
59 _ => None,
60 }
61}