use kdl::KdlNode;
use crate::ast::block_style::BlockStyle;
pub(crate) const PAGE_KNOWN_PROPS: &[&str] = &[
"id",
"name",
"w",
"h",
"background",
"bleed",
"margin-inner",
"margin_inner",
"margin-outer",
"margin_outer",
"margin-top",
"margin_top",
"margin-bottom",
"margin_bottom",
"baseline-grid",
"baseline_grid",
"line-jumps",
"line_jumps",
"parity",
"master",
];
use crate::ast::document::{Fold, Page, SafeZone, SafeZoneType};
use crate::ast::node::Node;
use crate::error::{ParseError, ParseErrorCode};
use super::block_style::transform_block_style;
use super::helpers::{
entry_to_dimension, entry_to_property_value, node_span, optional_dimension_prop,
optional_string_prop, required_string_prop,
};
use super::node::transform_node;
pub(super) fn transform_page(node: &KdlNode) -> Result<Page, ParseError> {
let id = required_string_prop(node, "id")?.to_owned();
let name = optional_string_prop(node, "name").map(str::to_owned);
let width = node
.entry("w")
.ok_or_else(|| {
ParseError::spanless(
ParseErrorCode::InvalidPropertyValue,
format!("page `{id}` is missing required property `w`"),
)
})
.and_then(|e| entry_to_dimension(e, "w"))?;
let height = node
.entry("h")
.ok_or_else(|| {
ParseError::spanless(
ParseErrorCode::InvalidPropertyValue,
format!("page `{id}` is missing required property `h`"),
)
})
.and_then(|e| entry_to_dimension(e, "h"))?;
let background = node
.entry("background")
.and_then(|e| entry_to_property_value(e).ok());
let bleed = optional_dimension_prop(node, "bleed");
let margin_inner = optional_dimension_prop(node, "margin-inner")
.or_else(|| optional_dimension_prop(node, "margin_inner"));
let margin_outer = optional_dimension_prop(node, "margin-outer")
.or_else(|| optional_dimension_prop(node, "margin_outer"));
let margin_top = optional_dimension_prop(node, "margin-top")
.or_else(|| optional_dimension_prop(node, "margin_top"));
let margin_bottom = optional_dimension_prop(node, "margin-bottom")
.or_else(|| optional_dimension_prop(node, "margin_bottom"));
let baseline_grid = optional_dimension_prop(node, "baseline-grid")
.or_else(|| optional_dimension_prop(node, "baseline_grid"));
let line_jumps = optional_string_prop(node, "line-jumps")
.or_else(|| optional_string_prop(node, "line_jumps"))
.map(str::to_owned);
let parity = optional_string_prop(node, "parity").map(str::to_owned);
let master = optional_string_prop(node, "master").map(str::to_owned);
let source_span = node_span(node);
let mut safe_zones: Vec<SafeZone> = Vec::new();
let mut folds: Vec<Fold> = Vec::new();
let mut block_styles: Vec<BlockStyle> = Vec::new();
let mut children: Vec<Node> = Vec::new();
if let Some(doc) = node.children() {
for child in doc.nodes() {
match child.name().value() {
"safe-zone" => safe_zones.push(transform_safe_zone(child)?),
"fold" => folds.push(transform_fold(child)?),
"block" => block_styles.push(transform_block_style(child)?),
_ => children.push(transform_node(child)?),
}
}
}
Ok(Page {
id,
name,
width,
height,
background,
bleed,
margin_inner,
margin_outer,
margin_top,
margin_bottom,
baseline_grid,
line_jumps,
parity,
master,
safe_zones,
folds,
block_styles,
children,
source_span,
})
}
fn transform_fold(node: &KdlNode) -> Result<Fold, ParseError> {
let id = required_string_prop(node, "id")?.to_owned();
let orientation = match optional_string_prop(node, "orientation") {
Some("horizontal") => "horizontal".to_owned(),
_ => "vertical".to_owned(),
};
let position = match node.entry("position") {
Some(e) => Some(entry_to_dimension(e, "position")?),
None => None,
};
Ok(Fold {
id,
orientation,
position,
source_span: node_span(node),
})
}
fn transform_safe_zone(node: &KdlNode) -> Result<SafeZone, ParseError> {
let id = required_string_prop(node, "id")?.to_owned();
let zone_type = match optional_string_prop(node, "type") {
Some("required") => SafeZoneType::Required,
_ => SafeZoneType::Exclusion,
};
let x = node
.entry("x")
.ok_or_else(|| {
ParseError::spanless(
ParseErrorCode::InvalidPropertyValue,
format!("safe-zone `{id}` is missing required property `x`"),
)
})
.and_then(|e| entry_to_dimension(e, "x"))?;
let y = node
.entry("y")
.ok_or_else(|| {
ParseError::spanless(
ParseErrorCode::InvalidPropertyValue,
format!("safe-zone `{id}` is missing required property `y`"),
)
})
.and_then(|e| entry_to_dimension(e, "y"))?;
let w = node
.entry("w")
.ok_or_else(|| {
ParseError::spanless(
ParseErrorCode::InvalidPropertyValue,
format!("safe-zone `{id}` is missing required property `w`"),
)
})
.and_then(|e| entry_to_dimension(e, "w"))?;
let h = node
.entry("h")
.ok_or_else(|| {
ParseError::spanless(
ParseErrorCode::InvalidPropertyValue,
format!("safe-zone `{id}` is missing required property `h`"),
)
})
.and_then(|e| entry_to_dimension(e, "h"))?;
let label = optional_string_prop(node, "label").map(str::to_owned);
Ok(SafeZone {
id,
zone_type,
x,
y,
w,
h,
label,
source_span: node_span(node),
})
}