use std::collections::BTreeMap;
use serde_json::Value;
use super::TemplateError;
#[derive(Debug, Clone, PartialEq)]
enum Answer {
Value(Value),
Text(String),
Unset,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Answers {
entries: BTreeMap<String, Answer>,
}
impl Answers {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn from_yaml(text: &str) -> Result<Self, TemplateError> {
let parsed: Value =
serde_norway::from_str(text).map_err(|error| TemplateError::MalformedAnswers {
message: format!("it is not YAML a mapping of answers can be read from: {error}"),
})?;
match parsed {
Value::Null => Ok(Self::new()),
Value::Object(entries) => Ok(Self {
entries: entries
.into_iter()
.map(|(name, value)| (name, Answer::Value(value)))
.collect(),
}),
_ => Err(TemplateError::MalformedAnswers {
message: "its top level is not a mapping from variable name to value".to_owned(),
}),
}
}
pub fn set(&mut self, name: impl Into<String>, value: Value) -> &mut Self {
self.entries.insert(name.into(), Answer::Value(value));
self
}
pub fn set_text(&mut self, name: impl Into<String>, text: impl Into<String>) -> &mut Self {
self.entries.insert(name.into(), Answer::Text(text.into()));
self
}
pub fn unset(&mut self, name: impl Into<String>) -> &mut Self {
self.entries.insert(name.into(), Answer::Unset);
self
}
#[must_use]
pub fn overlay(&self, later: &Self) -> Self {
let mut entries = self.entries.clone();
for (name, answer) in &later.entries {
entries.insert(name.clone(), answer.clone());
}
Self { entries }
}
pub fn names(&self) -> impl Iterator<Item = &str> {
self.entries
.iter()
.filter(|(_, answer)| !matches!(answer, Answer::Unset))
.map(|(name, _)| name.as_str())
}
pub(super) fn given(&self, name: &str) -> Option<Given<'_>> {
match self.entries.get(name)? {
Answer::Value(value) => Some(Given::Value(value)),
Answer::Text(text) => Some(Given::Text(text)),
Answer::Unset => None,
}
}
}
pub(super) enum Given<'a> {
Value(&'a Value),
Text(&'a str),
}