use super::{Lowerer, bool_of, str_of, usize_of};
use crate::{
Spanned,
grammar::{FmFormat, Format, FrontmatterConstraint, IdTemplate, Shape, shape_of},
};
impl Lowerer<'_> {
pub fn shape(&mut self, node: &Spanned) -> Option<Shape> {
match shape_of(node, &self.types, self.regexes) {
Ok(s) => s,
Err(e) => {
self.fail_with(node.line, e);
None
}
}
}
pub fn check_key(
&mut self,
line: usize,
key: &str,
shape: Option<&Shape>,
format: Option<&Format>,
) {
let shapes: Vec<&Shape> = match (shape, format) {
(Some(s), _) => vec![s],
(None, Some(f)) => f.shapes().map(|(_, s)| s).collect(),
(None, None) => vec![],
};
if shapes.is_empty() || shapes.iter().any(|s| !s.has_group(key)) {
self.fail(
line,
"TL_31",
format!("`key` `{key}` names no hole or named group of the rule's shape"),
);
}
}
pub fn frontmatter_constraint(&mut self, node: &Spanned, key: &str) -> FrontmatterConstraint {
if key != "id" {
self.explained(node, &["pattern", "template"], false);
}
self.one_voice(node);
let shape = self.shape(node);
let width = usize_of(node, "seq-width");
let mut id_template = None;
match (&shape, key) {
(Some(Shape::Template(t)), "id") => {
for other in ["const", "enum", "format", "list", "min", "max"] {
if let Some(o) = node.get(other) {
self.fail(
o.line,
"TL_115",
format!(
"`{other}` beside the `id` template: `id` takes `template` alone"
),
);
}
}
match IdTemplate::from_template(t, width.unwrap_or(3), &self.lifecycle) {
Ok(parsed) => {
if width.is_some() && parsed.seq_width().is_none() {
self.fail(
node.line,
"TL_116",
"`seq-width` beside a template with no `seq` hole",
);
}
id_template = Some(parsed);
}
Err(e) => self.fail(node.line, e.criterion, e.message),
}
}
(None, "id") if node.get("const").is_some() => {
match IdTemplate::fixed(&str_of(node, "const").unwrap_or_default()) {
Ok(fixed) => id_template = Some(fixed),
Err(e) => self.fail(node.line, e.criterion, e.message),
}
}
_ => {
if width.is_some() {
self.fail(
node.line,
"TL_116",
"`seq-width` applies to the `id` template alone",
);
}
}
}
if let Some(min) = usize_of(node, "min") {
self.check_bounds(node.line, min, usize_of(node, "max"));
}
FrontmatterConstraint {
common: self.common(node),
const_: node.get("const").and_then(|c| c.scalar_text()),
shape,
enum_: node
.get("enum")
.and_then(|e| e.as_seq())
.map(|items| items.iter().filter_map(|i| i.scalar_text()).collect()),
format: str_of(node, "format").map(|f| match f.as_str() {
"url" => FmFormat::Url,
"slug" => FmFormat::Slug,
_ => FmFormat::Host(f),
}),
id_template,
list: bool_of(node, "list"),
min: usize_of(node, "min"),
max: usize_of(node, "max"),
optional: bool_of(node, "optional"),
example: str_of(node, "example"),
guidance: str_of(node, "guidance"),
}
}
}