use sup_xml_core::xpath::Expr;
#[derive(Clone, Debug)]
pub struct QName {
pub prefix: Option<String>,
pub local: String,
pub uri: String,
}
impl QName {
pub fn to_qname_string(&self) -> String {
match &self.prefix {
Some(p) => format!("{p}:{}", self.local),
None => self.local.clone(),
}
}
}
#[derive(Clone, Debug)]
pub enum AvtPart {
Literal(String),
Expr(Expr),
}
#[derive(Clone, Debug, Default)]
pub struct Avt {
pub parts: Vec<AvtPart>,
}
impl Avt {
pub fn is_literal(&self) -> bool {
self.parts.iter().all(|p| matches!(p, AvtPart::Literal(_)))
}
pub fn literal(s: &str) -> Self {
Avt { parts: vec![AvtPart::Literal(s.to_string())] }
}
pub fn as_literal(&self) -> Option<String> {
let mut out = String::new();
for p in &self.parts {
match p {
AvtPart::Literal(s) => out.push_str(s),
_ => return None,
}
}
Some(out)
}
}
#[derive(Clone, Debug)]
pub struct Template {
pub match_pattern: Option<Expr>,
pub name: Option<QName>,
pub mode: Option<QName>,
pub modes: Vec<QName>,
pub modes_match_all: bool,
pub priority: Option<f64>,
pub import_precedence: i32,
pub source_path: Vec<u32>,
pub params: Vec<Param>,
pub body: Vec<Instr>,
pub as_type: Option<String>,
}
#[derive(Clone, Debug)]
pub struct Param {
pub name: QName,
pub select: Option<Expr>,
pub body: Vec<Instr>,
pub tunnel: bool,
pub as_type: Option<String>,
pub required: bool,
}
#[derive(Clone, Debug)]
pub struct Variable {
pub name: QName,
pub select: Option<Expr>,
pub body: Vec<Instr>,
pub as_type: Option<String>,
pub base_uri: Option<String>,
pub visibility: Option<String>,
}
#[derive(Clone, Debug)]
pub enum Instr {
LiteralElement {
name: QName,
attributes: Vec<(QName, Avt)>,
namespaces: Vec<(Option<String>, String)>,
use_attribute_sets: Vec<QName>,
schema_type: Option<(String, String)>,
body: Vec<Instr>,
},
LiteralText { text: String, dose: bool },
ApplyTemplates {
select: Option<Expr>,
mode: Option<QName>,
sort: Vec<Sort>,
with_params: Vec<WithParam>,
mode_current: bool,
},
ApplyImports { with_params: Vec<WithParam> },
NextMatch {
with_params: Vec<WithParam>,
},
CallTemplate {
name: QName,
with_params: Vec<WithParam>,
},
Choose {
whens: Vec<(Expr, Vec<Instr>)>,
otherwise: Option<Vec<Instr>>,
},
If {
test: Expr,
body: Vec<Instr>,
},
ForEach {
select: Expr,
sort: Vec<Sort>,
body: Vec<Instr>,
},
Iterate {
select: Expr,
params: Vec<Param>,
on_completion: Vec<Instr>,
body: Vec<Instr>,
},
NextIteration {
with_params: Vec<WithParam>,
},
Break {
select: Option<Expr>,
body: Vec<Instr>,
},
ValueOf {
select: Expr,
dose: bool,
separator: Option<Avt>,
},
ValueOfBody {
body: Vec<Instr>,
dose: bool,
separator: Option<Avt>,
},
Copy {
use_attribute_sets: Vec<QName>,
body: Vec<Instr>,
copy_namespaces: bool,
},
CopyOf {
select: Expr,
copy_namespaces: bool,
},
Element {
name: Avt, namespace: Option<Avt>,
use_attribute_sets: Vec<QName>,
body: Vec<Instr>,
in_scope_namespaces: Vec<(Option<String>, String)>,
},
Attribute {
name: Avt,
namespace: Option<Avt>,
select: Option<Expr>,
separator: Option<Avt>,
body: Vec<Instr>,
in_scope_namespaces: Vec<(Option<String>, String)>,
schema_type: Option<(String, String)>,
},
Comment {
select: Option<Expr>,
body: Vec<Instr>,
},
ProcessingInstruction {
name: Avt,
select: Option<Expr>,
body: Vec<Instr>,
},
Number {
value: Option<Expr>,
select: Option<Expr>,
level: NumberLevel,
count: Option<Expr>,
from: Option<Expr>,
format: Avt,
grouping_separator: Option<Avt>,
grouping_size: Option<Avt>,
ordinal: Option<Avt>,
lang: Option<Avt>,
letter_value: Option<Avt>,
start_at: Option<Avt>,
},
Variable(Variable),
Message {
terminate: Option<Avt>,
body: Vec<Instr>,
},
Fallback { body: Vec<Instr> },
Sequence { select: Expr },
Map { body: Vec<Instr> },
MapEntry { key: Expr, select: Option<Expr>, body: Vec<Instr> },
ForEachGroup {
select: Expr,
kind: GroupingKind,
key: Expr,
sort: Vec<Sort>,
body: Vec<Instr>,
collation: Option<String>,
},
SourceDocument {
href: Avt,
body: Vec<Instr>,
},
OnEmpty {
body: Vec<Instr>,
},
OnNonEmpty {
body: Vec<Instr>,
},
WherePopulated {
body: Vec<Instr>,
},
Fork {
body: Vec<Instr>,
},
Evaluate {
xpath: Expr,
context_item: Option<Expr>,
with_params: Vec<WithParam>,
schema_aware: bool,
},
Merge {
sources: Vec<MergeSource>,
action: Vec<Instr>,
},
AnalyzeString {
select: Expr,
regex: Avt,
flags: Avt,
matching: Vec<Instr>,
non_matching: Vec<Instr>,
},
PerformSort {
select: Option<Expr>,
sort: Vec<Sort>,
body: Vec<Instr>,
},
Document {
body: Vec<Instr>,
},
ResultDocument {
href: Avt,
format: Option<Avt>,
format_namespaces: Vec<(Option<String>, String)>,
body: Vec<Instr>,
},
Namespace {
name: Avt,
select: Option<Expr>,
body: Vec<Instr>,
},
Try {
body: Vec<Instr>,
catches: Vec<TryCatch>,
},
Unsupported {
name: String,
fallback: Vec<Instr>,
},
}
#[derive(Clone, Debug)]
pub struct TryCatch {
pub errors: Vec<CatchMatcher>,
pub body: Vec<Instr>,
}
#[derive(Clone, Debug)]
pub enum CatchMatcher {
Any,
LocalNameOnly(String),
PrefixWildcard(String),
QName(QName),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GroupingKind {
By,
Adjacent,
StartingWith,
EndingWith,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum NumberLevel {
#[default]
Single,
Any,
Multiple,
}
#[derive(Clone, Debug)]
pub struct Sort {
pub select: Option<Expr>, pub lang: Option<Avt>,
pub data_type: Option<Avt>, pub order: Option<Avt>, pub case_order: Option<Avt>, pub collation: Option<Avt>,
}
#[derive(Clone, Debug)]
pub struct UsePackage {
pub name: String,
pub version: Option<String>,
pub overrides: Box<StylesheetAst>,
}
#[derive(Clone, Debug)]
pub struct AccumulatorDecl {
pub name: QName,
pub initial_value: Expr,
pub rules: Vec<AccumulatorRule>,
}
#[derive(Clone, Debug)]
pub struct AccumulatorRule {
pub match_pattern: Expr,
pub phase: AccumulatorPhase,
pub select: Option<Expr>,
pub body: Vec<Instr>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AccumulatorPhase { Start, End }
#[derive(Clone, Debug)]
pub struct MergeSource {
pub name: Option<String>,
pub select: Expr,
pub for_each_source: Option<Expr>,
pub keys: Vec<Sort>,
}
#[derive(Clone, Debug)]
pub struct WithParam {
pub name: QName,
pub select: Option<Expr>,
pub body: Vec<Instr>,
pub tunnel: bool,
pub as_type: Option<String>,
}
#[derive(Clone, Debug, Default)]
pub struct OutputSpec {
pub method: Option<String>,
pub encoding: Option<String>,
pub indent: Option<bool>,
pub omit_xml_declaration: Option<bool>,
pub standalone: Option<bool>,
pub cdata_section_elements: Vec<QName>,
pub media_type: Option<String>,
pub doctype_public: Option<String>,
pub doctype_system: Option<String>,
pub version: Option<String>,
pub use_character_maps: Vec<QName>,
}
#[derive(Clone, Debug)]
pub struct Key {
pub name: QName,
pub matcher: Expr,
pub use_: Expr,
pub body: Vec<Instr>,
pub collation: Option<String>,
}
#[derive(Clone, Debug)]
pub struct AttributeSet {
pub name: QName,
pub use_attribute_sets: Vec<QName>,
pub attributes: Vec<Instr>,
pub import_precedence: i32,
}
#[derive(Clone, Debug)]
pub enum WhitespaceRule {
Strip(QName, i32),
Preserve(QName, i32),
}
#[derive(Clone, Debug, Default)]
pub struct StylesheetAst {
pub version: String,
pub namespaces: std::collections::HashMap<String, String>,
pub namespace_aliases: Vec<(String, String, Option<String>)>,
pub templates: Vec<Template>,
pub global_variables: Vec<Variable>,
pub global_params: Vec<Param>,
pub keys: Vec<Key>,
pub attribute_sets: Vec<AttributeSet>,
pub decimal_formats: std::collections::HashMap<String, crate::format_number::DecimalFormat>,
pub decimal_format_explicit: std::collections::HashMap<String, u16>,
pub decimal_format_conflicts: std::collections::HashMap<String, u16>,
pub decimal_format_attr_prec: std::collections::HashMap<String, [i32; 10]>,
pub whitespace_rules: Vec<WhitespaceRule>,
pub outputs: Vec<OutputSpec>,
pub includes: Vec<String>,
pub include_positions: Vec<u32>,
pub imports: Vec<String>,
pub documents_to_load: Vec<String>,
pub functions: Vec<UserFunction>,
pub character_maps: Vec<CharacterMap>,
pub xml_base: Option<String>,
pub accumulators: Vec<AccumulatorDecl>,
pub use_packages: Vec<UsePackage>,
pub modes: Vec<ModeDecl>,
pub input_type_annotations: Vec<String>,
pub schema_imports: Vec<(Option<String>, String)>,
pub inline_schemas: Vec<String>,
#[cfg(feature = "xsd")]
pub schemas: Vec<std::sync::Arc<sup_xml_core::xsd::Schema>>,
}
#[derive(Clone, Debug)]
pub struct ModeDecl {
pub name: Option<QName>,
pub on_no_match: OnNoMatch,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum OnNoMatch {
#[default]
TextOnlyCopy,
DeepSkip,
ShallowSkip,
ShallowCopy,
DeepCopy,
Fail,
}
#[derive(Clone, Debug)]
pub struct CharacterMap {
pub name: QName,
pub use_character_maps: Vec<QName>,
pub mappings: Vec<(char, String)>,
}
#[derive(Clone, Debug)]
pub struct UserFunction {
pub name: QName,
pub params: Vec<Param>,
pub body: Vec<Instr>,
pub as_type: Option<String>,
pub visibility: Option<String>,
}