1use 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
22pub 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}