1use std::borrow::ToOwned;
4use std::collections::HashMap;
5use std::collections::hash_map::Entry;
6use std::ops::Index;
7use std::iter;
8
9use sxd_document::XmlChar;
10
11use ::{Value, str_to_num};
12use ::context;
13use ::nodeset::Nodeset;
14
15pub trait Function {
17 fn evaluate<'c, 'd>(&self,
20 context: &context::Evaluation<'c, 'd>,
21 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>;
22}
23
24#[derive(Debug, Copy, Clone, PartialEq, Hash)]
26pub enum ArgumentType {
27 Boolean,
28 Number,
29 String,
30 Nodeset,
31}
32
33impl<'a> From<&'a Value<'a>> for ArgumentType {
34 fn from(other: &'a Value<'a>) -> ArgumentType {
35 match *other {
36 Value::Boolean(..) => ArgumentType::Boolean,
37 Value::Number(..) => ArgumentType::Number,
38 Value::String(..) => ArgumentType::String,
39 Value::Nodeset(..) => ArgumentType::Nodeset,
40 }
41 }
42}
43
44quick_error! {
45 #[derive(Debug, Clone, PartialEq, Hash)]
47 pub enum Error {
48 TooManyArguments { expected: usize, actual: usize } {
49 description("too many arguments")
50 display("too many arguments, expected {} but had {}", expected, actual)
51 }
52 NotEnoughArguments { expected: usize, actual: usize } {
53 description("not enough arguments")
54 display("not enough arguments, expected {} but had {}", expected, actual)
55 }
56 ArgumentMissing {
57 description("attempted to use an argument that was not present")
58 }
59 ArgumentNotANodeset { actual: ArgumentType } {
60 description("argument was not a nodeset")
61 display("argument was expected to be a nodeset but was a {:?}", actual)
62 }
63 Other(what: String) {
64 description("an error occurred while evaluating a function")
65 display("could not evaluate function: {}", what)
66 }
67 }
68}
69
70impl Error {
71 fn not_a_nodeset(actual: &Value) -> Error {
72 Error::ArgumentNotANodeset { actual: actual.into() }
73 }
74}
75
76pub struct Args<'d>(pub Vec<Value<'d>>);
79
80impl<'d> Args<'d> {
81 pub fn len(&self) -> usize { self.0.len() }
82 pub fn is_empty(&self) -> bool { self.0.is_empty() }
83
84 pub fn at_least(&self, minimum: usize) -> Result<(), Error> {
86 let actual = self.0.len();
87 if actual < minimum {
88 Err(Error::NotEnoughArguments { expected: minimum, actual: actual })
89 } else {
90 Ok(())
91 }
92 }
93
94 pub fn at_most(&self, maximum: usize) -> Result<(), Error> {
96 let actual = self.0.len();
97 if actual > maximum {
98 Err(Error::TooManyArguments { expected: maximum, actual: actual })
99 } else {
100 Ok(())
101 }
102 }
103
104 pub fn exactly(&self, expected: usize) -> Result<(), Error> {
106 let actual = self.0.len();
107 if actual < expected {
108 Err(Error::NotEnoughArguments { expected: expected, actual: actual })
109 } else if actual > expected {
110 Err(Error::TooManyArguments { expected: expected, actual: actual })
111 } else {
112 Ok(())
113 }
114 }
115
116 fn into_strings(self) -> Vec<String> {
118 self.0.into_iter().map(Value::into_string).collect()
119 }
120
121 pub fn pop_boolean(&mut self) -> Result<bool, Error> {
124 let v = self.0.pop().ok_or(Error::ArgumentMissing)?;
125 Ok(v.into_boolean())
126 }
127
128 pub fn pop_number(&mut self) -> Result<f64, Error> {
131 let v = self.0.pop().ok_or(Error::ArgumentMissing)?;
132 Ok(v.into_number())
133 }
134
135 pub fn pop_string(&mut self) -> Result<String, Error> {
138 let v = self.0.pop().ok_or(Error::ArgumentMissing)?;
139 Ok(v.into_string())
140 }
141
142 pub fn pop_nodeset(&mut self) -> Result<Nodeset<'d>, Error> {
146 let v = self.0.pop().ok_or(Error::ArgumentMissing)?;
147 match v {
148 Value::Nodeset(v) => Ok(v),
149 a => Err(Error::not_a_nodeset(&a)),
150 }
151 }
152
153 fn pop_value_or_context_node<'c>(&mut self, context: &context::Evaluation<'c, 'd>) -> Value<'d> {
156 self.0.pop()
157 .unwrap_or_else(|| Value::Nodeset(nodeset![context.node]))
158 }
159
160 fn pop_string_value_or_context_node(&mut self, context: &context::Evaluation) -> String {
165 self.0.pop()
166 .map(Value::into_string)
167 .unwrap_or_else(|| context.node.string_value())
168 }
169
170 fn pop_nodeset_or_context_node<'c>(&mut self, context: &context::Evaluation<'c, 'd>)
175 -> Result<Nodeset<'d>, Error>
176 {
177 match self.0.pop() {
178 Some(Value::Nodeset(ns)) => Ok(ns),
179 Some(arg) => Err(Error::not_a_nodeset(&arg)),
180 None => Ok(nodeset![context.node]),
181 }
182 }
183}
184
185impl<'d> Index<usize> for Args<'d> {
186 type Output = Value<'d>;
187
188 fn index(&self, index: usize) -> &Value<'d> { self.0.index(index) }
189}
190
191struct Last;
192
193impl Function for Last {
194 fn evaluate<'c, 'd>(&self,
195 context: &context::Evaluation<'c, 'd>,
196 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
197 {
198 let args = Args(args);
199 try!(args.exactly(0));
200 Ok(Value::Number(context.size as f64))
201 }
202}
203
204struct Position;
205
206impl Function for Position {
207 fn evaluate<'c, 'd>(&self,
208 context: &context::Evaluation<'c, 'd>,
209 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
210 {
211 let args = Args(args);
212 try!(args.exactly(0));
213 Ok(Value::Number(context.position as f64))
214 }
215}
216
217struct Count;
218
219impl Function for Count {
220 fn evaluate<'c, 'd>(&self,
221 _context: &context::Evaluation<'c, 'd>,
222 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
223 {
224 let mut args = Args(args);
225 try!(args.exactly(1));
226 let arg = try!(args.pop_nodeset());
227 Ok(Value::Number(arg.size() as f64))
228 }
229}
230
231struct LocalName;
232
233impl Function for LocalName {
234 fn evaluate<'c, 'd>(&self,
235 context: &context::Evaluation<'c, 'd>,
236 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
237 {
238 let mut args = Args(args);
239 try!(args.at_most(1));
240 let arg = try!(args.pop_nodeset_or_context_node(context));
241 let name =
242 arg.document_order_first()
243 .and_then(|n| n.expanded_name())
244 .map(|q| q.local_part())
245 .unwrap_or("");
246 Ok(Value::String(name.to_owned()))
247 }
248}
249
250struct NamespaceUri;
251
252impl Function for NamespaceUri {
253 fn evaluate<'c, 'd>(&self,
254 context: &context::Evaluation<'c, 'd>,
255 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
256 {
257 let mut args = Args(args);
258 try!(args.at_most(1));
259 let arg = try!(args.pop_nodeset_or_context_node(context));
260 let name =
261 arg.document_order_first()
262 .and_then(|n| n.expanded_name())
263 .and_then(|q| q.namespace_uri())
264 .unwrap_or("");
265 Ok(Value::String(name.to_owned()))
266 }
267}
268
269struct Name;
270
271impl Function for Name {
272 fn evaluate<'c, 'd>(&self,
273 context: &context::Evaluation<'c, 'd>,
274 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
275 {
276 let mut args = Args(args);
277 try!(args.at_most(1));
278 let arg = try!(args.pop_nodeset_or_context_node(context));
279 let name =
280 arg.document_order_first()
281 .and_then(|n| n.prefixed_name())
282 .unwrap_or_else(String::new);
283 Ok(Value::String(name))
284 }
285}
286
287struct StringFn;
288
289impl Function for StringFn {
290 fn evaluate<'c, 'd>(&self,
291 context: &context::Evaluation<'c, 'd>,
292 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
293 {
294 let mut args = Args(args);
295 try!(args.at_most(1));
296 let arg = args.pop_value_or_context_node(context);
297 Ok(Value::String(arg.string()))
298 }
299}
300
301struct Concat;
302
303impl Function for Concat {
304 fn evaluate<'c, 'd>(&self,
305 _context: &context::Evaluation<'c, 'd>,
306 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
307 {
308 let args = Args(args);
309 try!(args.at_least(2));
310 let args = args.into_strings();
311 Ok(Value::String(args.concat()))
312 }
313}
314
315struct TwoStringPredicate(fn(&str, &str) -> bool);
316
317impl Function for TwoStringPredicate {
318 fn evaluate<'c, 'd>(&self,
319 _context: &context::Evaluation<'c, 'd>,
320 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
321 {
322 let args = Args(args);
323 try!(args.exactly(2));
324 let args = args.into_strings();
325 let v = self.0(&args[0], &args[1]);
326 Ok(Value::Boolean(v))
327 }
328}
329
330fn starts_with() -> TwoStringPredicate {
331 fn imp(a: &str, b: &str) -> bool { str::starts_with(a, b) };
332 TwoStringPredicate(imp)
333}
334fn contains() -> TwoStringPredicate {
335 fn imp(a: &str, b: &str) -> bool { str::contains(a, b) };
336 TwoStringPredicate(imp)
337}
338
339struct SubstringCommon(for<'s> fn(&'s str, &'s str) -> &'s str);
340
341impl Function for SubstringCommon {
342 fn evaluate<'c, 'd>(&self,
343 _context: &context::Evaluation<'c, 'd>,
344 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
345 {
346 let args = Args(args);
347 try!(args.exactly(2));
348 let args = args.into_strings();
349 let s = self.0(&args[0], &args[1]);
350 Ok(Value::String(s.to_owned()))
351 }
352}
353
354fn substring_before() -> SubstringCommon {
355 fn inner<'a>(haystack: &'a str, needle: &'a str) -> &'a str {
356 match haystack.find(needle) {
357 Some(pos) => &haystack[..pos],
358 None => "",
359 }
360 }
361 SubstringCommon(inner)
362}
363
364fn substring_after() -> SubstringCommon {
365 fn inner<'a>(haystack: &'a str, needle: &'a str) -> &'a str {
366 match haystack.find(needle) {
367 Some(pos) => &haystack[pos + needle.len()..],
368 None => "",
369 }
370 }
371 SubstringCommon(inner)
372}
373
374struct Substring;
375
376impl Function for Substring {
377 fn evaluate<'c, 'd>(&self,
378 _context: &context::Evaluation<'c, 'd>,
379 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
380 {
381 let mut args = Args(args);
382 try!(args.at_least(2));
383 try!(args.at_most(3));
384
385 let len = if args.len() == 3 {
386 let len = try!(args.pop_number());
387 round_ties_to_positive_infinity(len)
388 } else {
389 ::std::f64::INFINITY
390 };
391
392 let start = try!(args.pop_number());
393 let start = round_ties_to_positive_infinity(start);
394 let s = try!(args.pop_string());
395
396 let chars = s.chars().enumerate();
397 let selected_chars = chars.filter_map(|(p, s)| {
398 let p = (p+1) as f64; if p >= start && p < start + len {
400 Some(s)
401 } else {
402 None
403 }
404 }).collect() ;
405
406 Ok(Value::String(selected_chars))
407 }
408}
409
410struct StringLength;
411
412impl Function for StringLength {
413 fn evaluate<'c, 'd>(&self,
414 context: &context::Evaluation<'c, 'd>,
415 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
416 {
417 let mut args = Args(args);
418 try!(args.at_most(1));
419 let arg = args.pop_string_value_or_context_node(context);
420 Ok(Value::Number(arg.chars().count() as f64))
421 }
422}
423
424struct NormalizeSpace;
425
426impl Function for NormalizeSpace {
427 fn evaluate<'c, 'd>(&self,
428 context: &context::Evaluation<'c, 'd>,
429 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
430 {
431 let mut args = Args(args);
432 try!(args.at_most(1));
433 let arg = args.pop_string_value_or_context_node(context);
434 let s: Vec<_> = arg.split(XmlChar::is_space_char).filter(|s| !s.is_empty()).collect();
436 let s = s.join(" ");
437 Ok(Value::String(s))
438 }
439}
440
441struct Translate;
442
443impl Function for Translate {
444 fn evaluate<'c, 'd>(&self,
445 _context: &context::Evaluation<'c, 'd>,
446 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
447 {
448 let mut args = Args(args);
449 try!(args.exactly(3));
450
451 let to = try!(args.pop_string());
452 let from = try!(args.pop_string());
453 let s = try!(args.pop_string());
454
455 let mut replacements = HashMap::new();
456 let pairs = from.chars().zip(to.chars().map(Some).chain(iter::repeat(None)));
457 for (from, to) in pairs {
458 if let Entry::Vacant(entry) = replacements.entry(from) {
459 entry.insert(to);
460 }
461 }
462
463 let s = s.chars().filter_map(|c| {
464 replacements.get(&c).cloned().unwrap_or_else(|| Some(c))
465 }).collect();
466
467 Ok(Value::String(s))
468 }
469}
470
471struct BooleanFn;
472
473impl Function for BooleanFn {
474 fn evaluate<'c, 'd>(&self,
475 _context: &context::Evaluation<'c, 'd>,
476 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
477 {
478 let args = Args(args);
479 try!(args.exactly(1));
480 Ok(Value::Boolean(args[0].boolean()))
481 }
482}
483
484struct Not;
485
486impl Function for Not {
487 fn evaluate<'c, 'd>(&self,
488 _context: &context::Evaluation<'c, 'd>,
489 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
490 {
491 let mut args = Args(args);
492 try!(args.exactly(1));
493 let arg = try!(args.pop_boolean());
494 Ok(Value::Boolean(!arg))
495 }
496}
497
498struct BooleanLiteral(bool);
499
500impl Function for BooleanLiteral {
501 fn evaluate<'c, 'd>(&self,
502 _context: &context::Evaluation<'c, 'd>,
503 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
504 {
505 let args = Args(args);
506 try!(args.exactly(0));
507 Ok(Value::Boolean(self.0))
508 }
509}
510
511fn true_fn() -> BooleanLiteral { BooleanLiteral(true) }
512fn false_fn() -> BooleanLiteral { BooleanLiteral(false) }
513
514struct NumberFn;
515
516impl Function for NumberFn {
517 fn evaluate<'c, 'd>(&self,
518 context: &context::Evaluation<'c, 'd>,
519 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
520 {
521 let mut args = Args(args);
522 try!(args.at_most(1));
523 let arg = args.pop_value_or_context_node(context);
524 Ok(Value::Number(arg.number()))
525 }
526}
527
528struct Sum;
529
530impl Function for Sum {
531 fn evaluate<'c, 'd>(&self,
532 _context: &context::Evaluation<'c, 'd>,
533 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
534 {
535 let mut args = Args(args);
536 try!(args.exactly(1));
537 let arg = try!(args.pop_nodeset());
538 let r = arg.iter().map(|n| str_to_num(&n.string_value())).fold(0.0, |acc, i| acc + i);
539 Ok(Value::Number(r))
540 }
541}
542
543struct NumberConvert(fn(f64) -> f64);
544
545impl Function for NumberConvert {
546 fn evaluate<'c, 'd>(&self,
547 _context: &context::Evaluation<'c, 'd>,
548 args: Vec<Value<'d>>) -> Result<Value<'d>, Error>
549 {
550 let mut args = Args(args);
551 try!(args.exactly(1));
552 let arg = try!(args.pop_number());
553 Ok(Value::Number(self.0(arg)))
554 }
555}
556
557fn floor() -> NumberConvert { NumberConvert(f64::floor) }
558fn ceiling() -> NumberConvert { NumberConvert(f64::ceil) }
559
560fn round_ties_to_positive_infinity(x: f64) -> f64 {
562 let y = x.floor();
563 if x == y {
564 x
565 } else {
566 let z = (2.0 * x - y).floor();
567 if x.is_sign_positive() ^ z.is_sign_positive() {
569 -z
570 } else {
571 z
572 }
573 }
574}
575
576fn round() -> NumberConvert { NumberConvert(round_ties_to_positive_infinity) }
577
578pub fn register_core_functions(context: &mut context::Context) {
582 context.set_function("last", Last);
583 context.set_function("position", Position);
584 context.set_function("count", Count);
585 context.set_function("local-name", LocalName);
586 context.set_function("namespace-uri", NamespaceUri);
587 context.set_function("name", Name);
588 context.set_function("string", StringFn);
589 context.set_function("concat", Concat);
590 context.set_function("starts-with", starts_with());
591 context.set_function("contains", contains());
592 context.set_function("substring-before", substring_before());
593 context.set_function("substring-after", substring_after());
594 context.set_function("substring", Substring);
595 context.set_function("string-length", StringLength);
596 context.set_function("normalize-space", NormalizeSpace);
597 context.set_function("translate", Translate);
598 context.set_function("boolean", BooleanFn);
599 context.set_function("not", Not);
600 context.set_function("true", true_fn());
601 context.set_function("false", false_fn());
602 context.set_function("number", NumberFn);
603 context.set_function("sum", Sum);
604 context.set_function("floor", floor());
605 context.set_function("ceiling", ceiling());
606 context.set_function("round", round());
607}
608
609#[cfg(test)]
610mod test {
611 use std::borrow::ToOwned;
612 use std::{fmt, f64};
613
614 use sxd_document::Package;
615
616 use ::{LiteralValue, Value};
617 use ::context;
618 use ::nodeset::Node;
619
620 use super::{
621 Function,
622 Error,
623 Last,
624 Position,
625 Count,
626 LocalName,
627 NamespaceUri,
628 Name,
629 StringFn,
630 Concat,
631 Substring,
632 StringLength,
633 NormalizeSpace,
634 Translate,
635 BooleanFn,
636 NumberFn,
637 Sum,
638 starts_with,
639 contains,
640 substring_before,
641 substring_after,
642 floor,
643 ceiling,
644 round,
645 };
646
647 macro_rules! args {
650 ( $($val:expr,)* ) => {
651 vec![
652 $( Value::from($val), )*
653 ]
654 };
655 ( $($val:expr),* ) => {
656 args![$($val, )*]
657 };
658 }
659
660 struct Setup<'d> {
661 context: context::Context<'d>,
662 }
663
664 impl<'d> Setup<'d> {
665 fn new() -> Setup<'d> {
666 Setup {
667 context: context::Context::without_core_functions(),
668 }
669 }
670
671 fn evaluate<N, F>(&self, node: N, f: F, args: Vec<Value<'d>>)
672 -> Result<Value<'d>, Error>
673 where N: Into<Node<'d>>,
674 F: Function
675 {
676 let context = context::Evaluation::new(&self.context, node.into());
677 f.evaluate(&context, args)
678 }
679 }
680
681 fn evaluate_literal<F, F2, T>(f: F, args: Vec<LiteralValue>, rf: F2) -> T
682 where F: Function,
683 F2: FnOnce(Result<Value, Error>) -> T,
684 {
685 let package = Package::new();
686 let doc = package.as_document();
687 let setup = Setup::new();
688
689 rf(setup.evaluate(doc.root(), f, args))
690 }
691
692 #[test]
693 fn last_returns_context_size() {
694 evaluate_literal(Last, args![], |r| {
695 assert_eq!(Ok(Value::Number(1.0)), r);
696 });
697 }
698
699 #[test]
700 fn position_returns_context_position() {
701 evaluate_literal(Position, args![], |r| {
702 assert_eq!(Ok(Value::Number(1.0)), r);
703 });
704 }
705
706 #[test]
707 fn count_counts_nodes_in_nodeset() {
708 let package = Package::new();
709 let doc = package.as_document();
710 let setup = Setup::new();
711
712 let r = setup.evaluate(doc.root(), Count, args![nodeset![doc.root()]]);
713
714 assert_eq!(Ok(Value::Number(1.0)), r);
715 }
716
717 #[test]
718 fn local_name_gets_name_of_element() {
719 let package = Package::new();
720 let doc = package.as_document();
721 let setup = Setup::new();
722
723 let e = doc.create_element(("uri", "wow"));
724 doc.root().append_child(e);
725
726 let r = setup.evaluate(doc.root(), LocalName, args![nodeset![e]]);
727
728 assert_eq!(Ok(Value::String("wow".to_owned())), r);
729 }
730
731 #[test]
732 fn local_name_is_empty_for_empty_nodeset() {
733 evaluate_literal(LocalName, args![nodeset![]], |r| {
734 assert_eq!(Ok(Value::String("".to_owned())), r);
735 });
736 }
737
738 #[test]
739 fn namespace_uri_gets_uri_of_element() {
740 let package = Package::new();
741 let doc = package.as_document();
742 let setup = Setup::new();
743
744 let e = doc.create_element(("uri", "wow"));
745 doc.root().append_child(e);
746
747 let r = setup.evaluate(doc.root(), NamespaceUri, args![nodeset![e]]);
748
749 assert_eq!(Ok(Value::String("uri".to_owned())), r);
750 }
751
752 #[test]
753 fn name_uses_declared_prefix() {
754 let package = Package::new();
755 let doc = package.as_document();
756 let setup = Setup::new();
757
758 let e = doc.create_element(("uri", "wow"));
759 e.register_prefix("prefix", "uri");
760 doc.root().append_child(e);
761
762 let r = setup.evaluate(doc.root(), Name, args![nodeset![e]]);
763
764 assert_eq!(Ok(Value::String("prefix:wow".to_owned())), r);
765 }
766
767 #[test]
768 fn string_converts_to_string() {
769 evaluate_literal(StringFn, args![true], |r| {
770 assert_eq!(Ok(Value::String("true".to_owned())), r);
771 });
772 }
773
774 #[test]
775 fn concat_combines_strings() {
776 evaluate_literal(Concat, args!["hello", " ", "world"], |r| {
777 assert_eq!(Ok(Value::String("hello world".to_owned())), r);
778 });
779 }
780
781 #[test]
782 fn starts_with_checks_prefixes() {
783 evaluate_literal(starts_with(), args!["hello", "he"], |r| {
784 assert_eq!(Ok(Value::Boolean(true)), r);
785 });
786 }
787
788 #[test]
789 fn contains_looks_for_a_needle() {
790 evaluate_literal(contains(), args!["astronomer", "ono"], |r| {
791 assert_eq!(Ok(Value::Boolean(true)), r);
792 });
793 }
794
795 #[test]
796 fn substring_before_slices_before() {
797 evaluate_literal(substring_before(), args!["1999/04/01", "/"], |r| {
798 assert_eq!(Ok(Value::String("1999".to_owned())), r);
799 });
800 }
801
802 #[test]
803 fn substring_after_slices_after() {
804 evaluate_literal(substring_after(), args!["1999/04/01", "/"], |r| {
805 assert_eq!(Ok(Value::String("04/01".to_owned())), r);
806 });
807 }
808
809 #[test]
810 fn substring_is_one_indexed() {
811 evaluate_literal(Substring, args!["あいうえお", 2.0], |r| {
812 assert_eq!(Ok(Value::String("いうえお".to_owned())), r);
813 });
814 }
815
816 #[test]
817 fn substring_has_optional_length() {
818 evaluate_literal(Substring, args!["あいうえお", 2.0, 3.0], |r| {
819 assert_eq!(Ok(Value::String("いうえ".to_owned())), r);
820 });
821 }
822
823 fn substring_test(s: &str, start: f64, len: f64) -> String {
824 evaluate_literal(Substring, args![s, start, len], |r| {
825 match r {
826 Ok(Value::String(s)) => s,
827 r => panic!("substring failed: {:?}", r),
828 }
829 })
830 }
831
832 #[test]
833 fn substring_rounds_values() {
834 assert_eq!("いうえ", substring_test("あいうえお", 1.5, 2.6));
835 }
836
837 #[test]
838 fn substring_is_a_window_of_the_characters() {
839 assert_eq!("あい", substring_test("あいうえお", 0.0, 3.0));
840 }
841
842 #[test]
843 fn substring_with_nan_start_is_empty() {
844 assert_eq!("", substring_test("あいうえお", f64::NAN, 3.0));
845 }
846
847 #[test]
848 fn substring_with_nan_len_is_empty() {
849 assert_eq!("", substring_test("あいうえお", 1.0, f64::NAN));
850 }
851
852 #[test]
853 fn substring_with_infinite_len_goes_to_end_of_string() {
854 assert_eq!("あいうえお", substring_test("あいうえお", -42.0, f64::INFINITY));
855 }
856
857 #[test]
858 fn substring_with_negative_infinity_start_is_empty() {
859 assert_eq!("", substring_test("あいうえお", f64::NEG_INFINITY, f64::INFINITY));
860 }
861
862 #[test]
863 fn string_length_counts_characters() {
864 evaluate_literal(StringLength, args!["日本語"], |r| {
865 assert_eq!(Ok(Value::Number(3.0)), r);
866 });
867 }
868
869 #[test]
870 fn normalize_space_removes_leading_space() {
871 evaluate_literal(NormalizeSpace, args!["\t hello"], |r| {
872 assert_eq!(Ok(Value::String("hello".to_owned())), r);
873 });
874 }
875
876 #[test]
877 fn normalize_space_removes_trailing_space() {
878 evaluate_literal(NormalizeSpace, args!["hello\r\n"], |r| {
879 assert_eq!(Ok(Value::String("hello".to_owned())), r);
880 });
881 }
882
883 #[test]
884 fn normalize_space_squashes_intermediate_space() {
885 evaluate_literal(NormalizeSpace, args!["hello\t\r\n world"], |r| {
886 assert_eq!(Ok(Value::String("hello world".to_owned())), r);
887 });
888 }
889
890 fn translate_test(s: &str, from: &str, to: &str) -> String {
891 evaluate_literal(Translate, args![s, from, to], |r| {
892 match r {
893 Ok(Value::String(s)) => s,
894 r => panic!("translate failed: {:?}", r)
895 }
896 })
897 }
898
899 #[test]
900 fn translate_replaces_characters() {
901 assert_eq!("イエ", translate_test("いえ", "あいうえお", "アイウエオ"));
902 }
903
904 #[test]
905 fn translate_removes_characters_without_replacement() {
906 assert_eq!("イ", translate_test("いえ", "あいうえお", "アイ"));
907 }
908
909 #[test]
910 fn translate_replaces_each_char_only_once() {
911 assert_eq!("b", translate_test("a", "ab", "bc"));
912 }
913
914 #[test]
915 fn translate_uses_first_replacement() {
916 assert_eq!("b", translate_test("a", "aa", "bc"));
917 }
918
919 #[test]
920 fn translate_ignores_extra_replacements() {
921 assert_eq!("b", translate_test("a", "a", "bc"));
922 }
923
924 #[test]
925 fn boolean_converts_to_boolean() {
926 evaluate_literal(BooleanFn, args!["false"], |r| {
927 assert_eq!(Ok(Value::Boolean(true)), r);
928 });
929 }
930
931 #[test]
932 fn number_converts_to_number() {
933 evaluate_literal(NumberFn, args![" -1.2 "], |r| {
934 assert_eq!(Ok(Value::Number(-1.2)), r);
935 });
936 }
937
938 #[test]
939 fn number_fails_with_nan() {
940 evaluate_literal(NumberFn, args![" nope "], |r| assert_number(f64::NAN, r));
941 }
942
943 #[test]
944 fn sum_adds_up_nodeset() {
945 let package = Package::new();
946 let doc = package.as_document();
947 let setup = Setup::new();
948
949 let c = doc.create_comment("-32.0");
950 let t = doc.create_text("98.7");
951
952 let r = setup.evaluate(doc.root(), Sum, args![nodeset![c, t]]);
953
954 assert_eq!(Ok(Value::Number(66.7)), r);
955 }
956
957 struct PedanticNumber(f64);
960
961 impl PedanticNumber {
962 fn non_nan_key(&self) -> (bool, bool, f64) {
963 (self.0.is_finite(), self.0.is_sign_positive(), self.0)
964 }
965 }
966
967 impl PartialEq for PedanticNumber {
968 fn eq(&self, other: &Self) -> bool {
969 if self.0.is_nan() {
970 other.0.is_nan()
971 } else {
972 self.non_nan_key() == other.non_nan_key()
973 }
974 }
975 }
976
977 impl fmt::Debug for PedanticNumber {
978 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
979 write!(f, "{{ {}, NaN: {}, finite: {}, positive: {} }}",
980 self.0,
981 self.0.is_nan(),
982 self.0.is_finite(),
983 self.0.is_sign_positive())
984 }
985 }
986
987 fn assert_number(expected: f64, actual: Result<Value, Error>) {
988 match actual {
989 Ok(Value::Number(n)) => assert_eq!(PedanticNumber(n), PedanticNumber(expected)),
990 _ => assert!(false, "{:?} did not evaluate correctly", actual),
991 }
992 }
993
994 #[test]
995 fn floor_rounds_down() {
996 evaluate_literal(floor(), args![199.99], |r| assert_number(199.0, r));
997 }
998
999 #[test]
1000 fn ceiling_rounds_up() {
1001 evaluate_literal(ceiling(), args![199.99], |r| assert_number(200.0, r));
1002 }
1003
1004 #[test]
1005 fn round_nan_to_nan() {
1006 evaluate_literal(round(), args![f64::NAN], |r| assert_number(f64::NAN, r));
1007 }
1008
1009 #[test]
1010 fn round_pos_inf_to_pos_inf() {
1011 evaluate_literal(round(), args![f64::INFINITY], |r| {
1012 assert_number(f64::INFINITY, r)
1013 });
1014 }
1015
1016 #[test]
1017 fn round_neg_inf_to_neg_inf() {
1018 evaluate_literal(round(), args![f64::NEG_INFINITY], |r| {
1019 assert_number(f64::NEG_INFINITY, r)
1020 });
1021 }
1022
1023 #[test]
1024 fn round_pos_zero_to_pos_zero() {
1025 evaluate_literal(round(), args![0.0], |r| assert_number(0.0, r));
1026 }
1027
1028 #[test]
1029 fn round_neg_zero_to_neg_zero() {
1030 evaluate_literal(round(), args![-0.0], |r| assert_number(-0.0, r));
1031 }
1032
1033 #[test]
1034 fn round_neg_zero_point_five_to_neg_zero() {
1035 evaluate_literal(round(), args![-0.5], |r| assert_number(-0.0, r));
1036 }
1037
1038 #[test]
1039 fn round_neg_five_to_neg_five() {
1040 evaluate_literal(round(), args![-5.0], |r| assert_number(-5.0, r));
1041 }
1042
1043 #[test]
1044 fn round_pos_zero_point_five_to_pos_one() {
1045 evaluate_literal(round(), args![0.5], |r| assert_number(1.0, r));
1046 }
1047}