1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use html5ever::tendril::TendrilSink;


pub mod value;
pub mod result;
pub mod parser;
pub mod tokens;
pub mod context;
pub mod factory;
pub mod functions;
pub mod expressions;
pub mod nodetest;

pub use nodetest::{NodeTest, NameTest};
pub use result::{Result, Error};
pub use value::{Value, Node, Nodeset};
pub use tokens::{ExprToken, AxisName, NodeType, Operator, PrincipalNodeType};
pub use context::Evaluation;
pub use parser::Tokenizer;
pub use factory::{Factory, Document};


pub static DEBUG: bool = false;


pub fn parse_doc<R: std::io::Read>(data: &mut R) -> Document {
	let parse: markup5ever_rcdom::RcDom = html5ever::parse_document(markup5ever_rcdom::RcDom::default(), Default::default())
		.from_utf8()
		.read_from(data)
		.expect("html5ever");

	Document::new(parse.document.into())
}


#[cfg(test)]
mod tests {
	use std::fs::File;

	pub use crate::nodetest::{NodeTest, NameTest};
	pub use crate::result::{Result, Error};
	pub use crate::value::{Value, Node, Nodeset};
	pub use crate::tokens::{ExprToken, AxisName, NodeType, Operator, PrincipalNodeType};
	pub use crate::context::Evaluation;
	pub use crate::parser::Tokenizer;
	pub use crate::factory::{Factory, Document};
	pub use crate::parse_doc;

	#[test]
	fn paths() {
		// let doc = parse_doc(&mut File::open("./doc/example.html").expect("File::open"));

		println!("Location Paths (Unabbreviated Syntax)");
		// assert_eq!(doc.evaluate("//head/title"), Ok(Value::Nodeset(vec![].into()))); // selects the document root (which is always the parent of the document element)
		// dbg!(doc.evaluate("self::para")); // selects the context node if it is a para element, and otherwise selects nothing
		// dbg!(doc.evaluate("child::para")); // selects the para element children of the context node
		// dbg!(doc.evaluate("child::*")); // selects all element children of the context node
		// dbg!(doc.evaluate("child::text()")); // selects all text node children of the context node
		// dbg!(doc.evaluate("child::node()")); // selects all the children of the context node, whatever their node type
		// dbg!(doc.evaluate("child::chapter/descendant::para")); // selects the para element descendants of the chapter element children of the context node
		// dbg!(doc.evaluate("child::*/child::para")); // selects all para grandchildren of the context node
		// dbg!(doc.evaluate("child::para[position()=1]")); // selects the first para child of the context node
		// dbg!(doc.evaluate("child::para[position()=last()]")); // selects the last para child of the context node
		// dbg!(doc.evaluate("child::para[position()=last()-1]")); // selects the last but one para child of the context node
		// dbg!(doc.evaluate("child::para[position()>1]")); // selects all the para children of the context node other than the first para child of the context node
		// dbg!(doc.evaluate("/child::doc/child::chapter[position()=5]/child::section[position()=2]")); // selects the second section of the fifth chapter of the doc document element
		// dbg!(doc.evaluate("child::para[attribute::type=\"warning\"]")); // selects all para children of the context node that have a type attribute with value warning
		// dbg!(doc.evaluate("child::para[attribute::type='warning'][position()=5]")); // selects the fifth para child of the context node that has a type attribute with value warning
		// dbg!(doc.evaluate("child::para[position()=5][attribute::type=\"warning\"]")); // selects the fifth para child of the context node if that child has a type attribute with value warning
		// dbg!(doc.evaluate("child::chapter[child::title='Introduction']")); // selects the chapter children of the context node that have one or more title children with string-value equal to Introduction
		// dbg!(doc.evaluate("child::chapter[child::title]")); // selects the chapter children of the context node that have one or more title children
		// dbg!(doc.evaluate("child::*[self::chapter or self::appendix]")); // selects the chapter and appendix children of the context node
		// dbg!(doc.evaluate("child::*[self::chapter or self::appendix][position()=last()]")); // selects the last chapter or appendix child of the context node
		// dbg!(doc.evaluate("attribute::name")); // selects the name attribute of the context node
		// dbg!(doc.evaluate("attribute::*")); // selects all the attributes of the context node
		// dbg!(doc.evaluate("ancestor::div")); // selects all div ancestors of the context node
		// dbg!(doc.evaluate("ancestor-or-self::div")); // selects the div ancestors of the context node and, if the context node is a div element, the context node as well
		// dbg!(doc.evaluate("following-sibling::chapter[position()=1]")); // selects the next chapter sibling of the context node
		// dbg!(doc.evaluate("preceding-sibling::chapter[position()=1]")); // selects the previous chapter sibling of the context node
		// dbg!(doc.evaluate("descendant::para")); // selects the para element descendants of the context node
		// dbg!(doc.evaluate("descendant-or-self::para")); // selects the para element descendants of the context node and, if the context node is a para element, the context node as well
		// dbg!(doc.evaluate("/descendant::para")); // selects all the para elements in the same document as the context node
		// dbg!(doc.evaluate("/descendant::olist/child::item")); // selects all the item elements that have an olist parent and that are in the same document as the context node
		// dbg!(doc.evaluate("/descendant::figure[position()=42]")); // selects the forty-second figure element in the document
	}

	#[test]
	fn paths_abbreviated() {
		// println!("Location Paths (Abbreviated Syntax)");
		// para selects the para element children of the context node
		// * selects all element children of the context node
		// text() selects all text node children of the context node
		// @name selects the name attribute of the context node
		// @* selects all the attributes of the context node
		// para[1] selects the first para child of the context node
		// para[last()] selects the last para child of the context node
		// */para selects all para grandchildren of the context node
		// /doc/chapter[5]/section[2] selects the second section of the fifth chapter of the doc
		// chapter//para selects the para element descendants of the chapter element children of the context node
		// //para selects all the para descendants of the document root and thus selects all para elements in the same document as the context node
		// //olist/item selects all the item elements in the same document as the context node that have an olist parent
		// . selects the context node
		// .//para selects the para element descendants of the context node
		// .. selects the parent of the context node
		// ../@lang selects the lang attribute of the parent of the context node
		// para[@type="warning"] selects all para children of the context node that have a type attribute with value warning
		// para[@type="warning"][5] selects the fifth para child of the context node that has a type attribute with value warning
		// para[5][@type="warning"] selects the fifth para child of the context node if that child has a type attribute with value warning
		// chapter[title="Introduction"] selects the chapter children of the context node that have one or more title children with string-value equal to Introduction
		// chapter[title] selects the chapter children of the context node that have one or more title children
		// employee[@secretary and @assistant] selects all the employee children of the context node that have both a secretary attribute and an assistant attribute
	}

	#[test]
	fn general_examples() {
		// println!("Examples");
		// dbg!(doc.evaluate("//*[@id='rcTEST']//*[contains(text(), 'TEST Interactive')]/../button[2]"));
		// dbg!(doc.evaluate("//*[@id='rcTEST']//*[contains(text(), 'TEST Interactive')]/..//*[contains(text(), 'Setting')]"));
		// dbg!(doc.evaluate("//*[@id='rcTEST']//*[contains(text(), 'TEST Interactive')]/following-sibling::button"));
		// dbg!(doc.evaluate("// address[@class='ng-scope ng-isolate-scope']//div[contains('Testing') and @id='msgTitle']"));
		// dbg!(doc.evaluate("//*[@name='myForm']//table[@id='tbl_ testdm']/tbody/tr/td[6]/"));
		// dbg!(doc.evaluate("input[@value='Open RFS']"));
		// dbg!(doc.evaluate("//*[@title='Songs List Applet']//table//td[contains(text(),'Author')]"));
		// dbg!(doc.evaluate("//*[@id='parameters']//*[@id='testUpdateTime']"));
		// dbg!(doc.evaluate("//*[@id='MODEL/PLAN']/div[1]/div[2]/div[1]/div[1]/widget/section/div[1]/div/div[1]/div/div/button[1]"));
		// dbg!(doc.evaluate("//*[contains(text(),'Watch Dial')]/../div/select[@data-ng-model='context.questions[subqts.subHandleSubId]']"));
		// dbg!(doc.evaluate("//*[@id='RESEARCH/PLAN']//*[contains(@id, 'A4')]/../../following-sibling::div[1]/div[1]/span[1]/span[1]"));
		// dbg!(doc.evaluate("//*[@id='ALARMDATA']//*[contains(@id, 'AFC2')]/../../preceding-sibling::div[1]/div[1]/span[1]/span[1]"));
		// dbg!(doc.evaluate("//*[@id='RESEARCH/REVIEW']//widget/section/div[1]/div/div[2]/div[1]/div[3]/div[1]//span[@class='details']"));
		// dbg!(doc.evaluate("//a[contains(.,'Parameter Data Manual Entry')]"));
		// dbg!(doc.evaluate("//*[contains(@style,'display: block; top:')]//input[@name='daterangepicker_end']"));
		// dbg!(doc.evaluate("//*[@id='dropdown-filter-serviceTools']/following-sibling::ul/descendant::a[text()='Notepad']"));
		// dbg!(doc.evaluate("//*[@id='dropdown-filter-serviceTools']/following-sibling::ul/descendant::a[text()='Trigger Dashboard']"));
		// dbg!(doc.evaluate("//h3[text()='Internal Debrief']"));
		// dbg!(doc.evaluate("//h3[contains(text(),'Helium Level')]/following-sibling::div/label/input"));
		// dbg!(doc.evaluate("//div[div[p[contains(text(),'Status')]]]/preceding-sibling::div/div/span[3]/span"));
		// dbg!(doc.evaluate("//*[@id='COUPLING']//*[contains(text(),'COUPLE Trend')]/../div/select"));
		// dbg!(doc.evaluate("//*[@id='ffaHeaderDropdown']//a[contains(text(),'Start Workflow')]"));
	}
}