use std::fmt::{Display, Formatter};
use kdl::{KdlEntry, KdlNode};
use serde::Serialize;
use crate::error::Result;
use crate::spec::context::ParsingContext;
use crate::spec::helpers::{string_entry, NodeHelper};
#[derive(Debug, Default, Clone, Serialize)]
#[non_exhaustive]
pub struct SpecView {
pub id: String,
pub name: String,
pub bin: String,
pub root: String,
pub all_globals: bool,
pub globals: Vec<String>,
}
impl SpecView {
pub(crate) fn parse(ctx: &ParsingContext, node: &NodeHelper<'_>) -> Result<Self> {
let id = node.arg(0)?.ensure_string()?;
let mut view = Self {
name: id.clone(),
bin: id.clone(),
id,
..Self::default()
};
for (key, value) in node.props() {
match key {
"name" => view.name = value.ensure_string()?,
"bin" => view.bin = value.ensure_string()?,
"root" => view.root = value.ensure_string()?,
"globals" => view.all_globals = value.ensure_bool()?,
key => bail_parse!(ctx, value.entry.span(), "unsupported view key {key}"),
}
}
for child in node.children() {
match child.name() {
"global" => {
for selector in child.args() {
view.globals.push(selector.ensure_string()?);
}
}
key => bail_parse!(
ctx,
child.node.name().span(),
"unsupported view value {key}"
),
}
}
if view.id.is_empty() {
bail_parse!(ctx, node.span(), "a view needs a non-empty identifier");
}
if view.name.is_empty() || view.bin.is_empty() {
bail_parse!(ctx, node.span(), "a view's name and bin cannot be empty");
}
if view.root.trim().is_empty() {
bail_parse!(
ctx,
node.span(),
"a view needs the command path it promotes in `root`"
);
}
Ok(view)
}
}
impl Display for SpecView {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut node = KdlNode::new("view");
node.push(string_entry(None, &self.id));
if self.name != self.id {
node.push(string_entry(Some("name"), &self.name));
}
if self.bin != self.name {
node.push(string_entry(Some("bin"), &self.bin));
}
node.push(string_entry(Some("root"), &self.root));
if self.all_globals {
node.push(KdlEntry::new_prop("globals", true));
}
if !self.globals.is_empty() {
let mut children = kdl::KdlDocument::new();
let mut global = KdlNode::new("global");
for selector in &self.globals {
global.push(string_entry(None, selector));
}
children.nodes_mut().push(global);
node.set_children(children);
}
write!(f, "{node}")
}
}