Skip to main content

sva_core/
set.rs

1// Concern: lays the variables a caller sets over a composition | Non-concern: what a variable's text means (sva-ast) | IO: (source, sets) -> an Overlay or CliError
2
3use std::collections::BTreeMap;
4
5use sva_ast::{Overlay, RESERVED_VARIABLES, Source, VARIABLES, variable};
6
7use crate::cli_error::CliError;
8
9pub fn set(written: &str) -> Result<(String, String), CliError> {
10    match written.split_once('=') {
11        Some((name, text)) => Ok((name.trim().to_string(), text.to_string())),
12        None => Err(CliError::usage(
13            "cli.bad_value",
14            format!(
15                "`--set {written}` has no `=`; it takes a variable's name and its text, as \
16                 `--set key=G3`"
17            ),
18        )),
19    }
20}
21
22/// `source` with each variable `sets` names, bare as `key` or `energy`, holding its text
23/// instead of its file's.
24pub fn overlaid<'a>(
25    source: &'a dyn Source,
26    sets: &[(String, String)],
27) -> Result<Overlay<'a>, CliError> {
28    let mut over = BTreeMap::new();
29    for (name, text) in sets {
30        if let Some(bare) = name.strip_prefix(&format!("{VARIABLES}/")) {
31            return Err(CliError::usage(
32                "render.set_path",
33                format!(
34                    "`{name}` is the variable's file; a set names the variable alone, as \
35                     `{bare}={text}`"
36                ),
37            ));
38        }
39        let held = source.get(&variable(name)).map_err(CliError::Io)?.is_some();
40        if !RESERVED_VARIABLES.contains(&name.as_str()) && !held {
41            let listed = source.paths().map_err(CliError::Io)?;
42            let mut there: Vec<&str> = listed
43                .found
44                .iter()
45                .filter_map(|p| p.strip_prefix(&format!("{VARIABLES}/")))
46                .collect();
47            there.sort_unstable();
48            return Err(CliError::usage(
49                "render.set_unreserved",
50                format!(
51                    "`{name}` names no file under `{VARIABLES}/`; a render sets a variable a file \
52                     there holds, or a reserved one ({}); the variables there are {}",
53                    RESERVED_VARIABLES.join(", "),
54                    match there.is_empty() {
55                        true => "none".to_string(),
56                        false => format!("`{}`", there.join("`, `")),
57                    }
58                ),
59            ));
60        }
61        if over.insert(variable(name), format!("{text}\n")).is_some() {
62            return Err(CliError::usage(
63                "render.set_twice",
64                format!("`{name}` is set twice; set each variable once"),
65            ));
66        }
67    }
68    Ok(Overlay::new(source, over))
69}