use crate::Item;
use crate::item::{Node, NodeType, Sequence, SequenceTrait};
use crate::output::OutputSpec;
use crate::transform::Transform;
use crate::transform::context::{Context, StaticContext};
use crate::value::{Value, ValueBuilder, ValueData};
use crate::xdmerror::{Error, ErrorKind};
use qualname::{NamespacePrefix, NamespaceUri, QName};
use std::rc::Rc;
use url::Url;
pub(crate) fn empty<N: Node>(_ctxt: &Context<N>) -> Result<Sequence<N>, Error> {
Ok(Sequence::new())
}
pub(crate) fn literal<N: Node>(_ctxt: &Context<N>, val: &Item<N>) -> Result<Sequence<N>, Error> {
Ok(vec![val.clone()])
}
pub(crate) fn literal_element<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
qn: &QName,
c: &Transform<N>,
) -> Result<Sequence<N>, Error> {
if ctxt.rd.is_none() {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context has no result document"),
));
}
let r = ctxt.rd.clone().unwrap();
let mut e = r.new_element(qn.clone())?;
ctxt.dispatch(stctxt, c)?.iter().try_for_each(|i| {
match i {
Item::Node(t) => {
match t.node_type() {
NodeType::Attribute => {
let new_att = r.new_attribute(t.name().unwrap(), t.value())?;
e.add_attribute(new_att)
} NodeType::Namespace => {
let new_ns = r.new_namespace(
t.as_namespace_uri()?.clone(),
t.as_namespace_prefix()?.cloned(),
t.is_in_scope(),
)?;
e.add_namespace(new_ns)
}
_ => e.push(t.deep_copy()?),
}
}
_ => {
let n = r.new_text(Rc::new(Value::from(i.to_string())))?;
e.push(n)
}
}
})?;
Ok(vec![Item::Node(e)])
}
pub(crate) fn element<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
qn: &Transform<N>,
c: &Transform<N>,
) -> Result<Sequence<N>, Error> {
if ctxt.rd.is_none() {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context has no result document"),
));
}
let r = ctxt.rd.clone().unwrap();
let qnavt = QName::try_from(ctxt.dispatch(stctxt, qn)?.to_string().as_str())
.map_err(|_| Error::new(ErrorKind::ParseError, "invalid QName"))?;
let mut e = r.new_element(qnavt)?;
ctxt.dispatch(stctxt, c)?.iter().try_for_each(|i| {
match i {
Item::Node(t) => match t.node_type() {
NodeType::Attribute => e.add_attribute(t.clone()),
_ => e.push(t.deep_copy()?),
},
_ => {
let n = r.new_text(Rc::new(Value::from(i.to_string())))?;
e.push(n)
}
}
})?;
Ok(vec![Item::Node(e)])
}
pub(crate) fn literal_text<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
t: &Transform<N>,
o: &OutputSpec,
) -> Result<Sequence<N>, Error> {
if ctxt.rd.is_none() {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context has no result document"),
));
}
let v = ctxt.dispatch(stctxt, t)?.to_string();
Ok(vec![Item::Node(
ctxt.rd.clone().unwrap().new_text(Rc::new(
ValueBuilder::new()
.value(ValueData::String(v))
.output(o.clone())
.build(),
))?,
)])
}
pub(crate) fn literal_attribute<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
qn: &QName,
t: &Transform<N>,
) -> Result<Sequence<N>, Error> {
if ctxt.rd.is_none() {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context has no result document"),
));
}
let v = ctxt.dispatch(stctxt, t)?;
let a = ctxt
.rd
.clone()
.unwrap()
.new_attribute(qn.clone(), Rc::new(Value::from(v.to_string())))?;
Ok(vec![Item::Node(a)])
}
pub(crate) fn literal_comment<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
t: &Transform<N>,
) -> Result<Sequence<N>, Error> {
if ctxt.rd.is_none() {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context has no result document"),
));
}
let a = ctxt
.rd
.clone()
.unwrap()
.new_comment(Rc::new(Value::from(ctxt.dispatch(stctxt, t)?.to_string())))?;
Ok(vec![Item::Node(a)])
}
pub(crate) fn literal_processing_instruction<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
name: &Transform<N>,
t: &Transform<N>,
) -> Result<Sequence<N>, Error> {
if ctxt.rd.is_none() {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context has no result document"),
));
}
let pi = ctxt.rd.clone().unwrap().new_processing_instruction(
Rc::new(Value::from(ctxt.dispatch(stctxt, name)?.to_string())),
Rc::new(Value::from(ctxt.dispatch(stctxt, t)?.to_string())),
)?;
Ok(vec![Item::Node(pi)])
}
pub(crate) fn namespace_declaration<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
p: &Option<Box<Transform<N>>>, u: &Transform<N>, in_scope: &Transform<N>, ) -> Result<Sequence<N>, Error> {
if ctxt.rd.is_none() {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context has no result document"),
));
}
let np = if let Some(pp) = p {
Some(
NamespacePrefix::try_from(ctxt.dispatch(stctxt, pp)?.to_string().as_str())
.map_err(|_| Error::new(ErrorKind::ParseError, "invalid namespapce prefix"))?,
)
} else {
None
};
Ok(vec![Item::Node(
ctxt.rd.as_ref().unwrap().new_namespace(
NamespaceUri::try_from(ctxt.dispatch(stctxt, u)?.to_string().as_str())
.map_err(|_| Error::new(ErrorKind::ParseError, "invalid namespapce URI"))?,
np,
ctxt.dispatch(stctxt, in_scope)?.to_bool(),
)?,
)])
}
pub(crate) fn set_attribute<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
atname: &QName,
v: &Transform<N>,
) -> Result<Sequence<N>, Error> {
if ctxt.rd.is_none() {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context has no result document"),
));
}
if ctxt.context_item.is_none() {
return Err(Error::new(ErrorKind::DynamicAbsent, "no context item"));
}
match &ctxt.context_item.as_ref().unwrap() {
Item::Node(n) => match n.node_type() {
NodeType::Element => {
let od = n.owner_document();
let attval = ctxt.dispatch(stctxt, v)?;
if attval.len() == 1 {
match attval.first() {
Some(Item::Value(av)) => {
n.add_attribute(od.new_attribute(atname.clone(), av.clone())?)?;
}
_ => {
n.add_attribute(od.new_attribute(
atname.clone(),
Rc::new(Value::from(attval.to_string())),
)?)?;
}
}
} else {
n.add_attribute(od.new_attribute(
atname.clone(),
Rc::new(Value::from(attval.to_string())),
)?)?;
}
}
_ => {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context item is not an element-type node"),
));
}
},
_ => {
return Err(Error::new(
ErrorKind::Unknown,
String::from("context item is not a node"),
));
}
}
Ok(vec![])
}
pub(crate) fn make_sequence<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
items: &Vec<Transform<N>>,
) -> Result<Sequence<N>, Error> {
items.iter().try_fold(vec![], |mut acc, i| {
let mut r = ctxt.dispatch(stctxt, i)?;
acc.append(&mut r);
Ok(acc)
})
}
pub(crate) fn copy<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
s: &Transform<N>,
c: &Transform<N>,
) -> Result<Sequence<N>, Error> {
let sel = ctxt.dispatch(stctxt, s)?;
let mut result: Sequence<N> = Vec::new();
for k in sel {
let cp = k.shallow_copy()?;
result.push(cp.clone());
if let Item::Node(mut im) = cp {
for j in ctxt.dispatch(stctxt, c)? {
match &j {
Item::Value(v) => im.push(im.new_text(v.clone())?)?,
Item::Node(n) => match n.node_type() {
NodeType::Attribute => im.add_attribute(n.clone())?,
_ => im.push(n.clone())?,
},
_ => {
return Err(Error::new(
ErrorKind::NotImplemented,
String::from("not yet implemented"),
));
}
}
}
}
}
Ok(result)
}
pub(crate) fn deep_copy<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
s: &Transform<N>,
) -> Result<Sequence<N>, Error> {
let sel = ctxt.dispatch(stctxt, s)?;
let mut result: Sequence<N> = Vec::new();
for k in sel {
result.push(k.deep_copy()?);
}
Ok(result)
}