mirror of
https://codeberg.org/icewind/galton.git
synced 2026-06-03 10:24:07 +02:00
switch to xee-xpath
This commit is contained in:
parent
9146f9deeb
commit
b42653f43a
3 changed files with 1342 additions and 330 deletions
1614
Cargo.lock
generated
1614
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
21
Cargo.toml
21
Cargo.toml
|
|
@ -9,20 +9,21 @@ rust-version = "1.85.0"
|
||||||
home = "0.5.11"
|
home = "0.5.11"
|
||||||
xattr = "1.6.1"
|
xattr = "1.6.1"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
toml = "0.9.8"
|
toml = "0.9.10"
|
||||||
regex = "1.12.1"
|
regex = "1.12.2"
|
||||||
thiserror = "2.0.17"
|
thiserror = "2.0.17"
|
||||||
clap = { version = "4.5.48", features = ["derive"] }
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
main_error = "0.1.2"
|
main_error = "0.1.2"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.20"
|
tracing-subscriber = "0.3.22"
|
||||||
notify-debouncer-full = "0.6.0"
|
notify-debouncer-full = "0.6.0"
|
||||||
ctrlc = "3.5.0"
|
ctrlc = "3.5.1"
|
||||||
sha2 = "0.11.0-rc.2"
|
sha2 = "0.11.0-rc.3"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
xrust = "1.3.0"
|
xee-xpath = "0.1.5"
|
||||||
|
xee-interpreter = "0.2.0"
|
||||||
notify-rust = "4.11.7"
|
notify-rust = "4.11.7"
|
||||||
open = "5.3.2"
|
open = "5.3.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,9 @@ use std::borrow::Cow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use xrust::parser::xml::parse as xmlparse;
|
use xee_interpreter::error::SpannedError;
|
||||||
use xrust::parser::xpath::parse;
|
use xee_xpath::error::DocumentsError;
|
||||||
use xrust::transform::context::{ContextBuilder, StaticContextBuilder};
|
use xee_xpath::{Documents, Queries, Query};
|
||||||
use xrust::trees::smite::RNode;
|
|
||||||
use xrust::{Error as XPathParseError, Item, Node, SequenceTrait};
|
|
||||||
|
|
||||||
pub struct XPathExtractor<'a> {
|
pub struct XPathExtractor<'a> {
|
||||||
file: &'a FileInfo,
|
file: &'a FileInfo,
|
||||||
|
|
@ -22,14 +20,16 @@ impl<'a> XPathExtractor<'a> {
|
||||||
|
|
||||||
impl Extractor for XPathExtractor<'_> {
|
impl Extractor for XPathExtractor<'_> {
|
||||||
fn extract<'this>(&'this self, field: &str) -> Option<Result<Cow<'this, str>, Box<dyn Error>>> {
|
fn extract<'this>(&'this self, field: &str) -> Option<Result<Cow<'this, str>, Box<dyn Error>>> {
|
||||||
let query = field
|
let query_string = field
|
||||||
.strip_prefix("xpath('")
|
.strip_prefix("xpath('")
|
||||||
.and_then(|query| query.strip_suffix("')"))?;
|
.and_then(|query| query.strip_suffix("')"))?;
|
||||||
let transform = match parse::<RNode>(query, None) {
|
|
||||||
Ok(transform) => transform,
|
let queries = Queries::default();
|
||||||
|
let query = match queries.one(query_string, |doc, item| Ok(item.string_value(doc.xot())?)) {
|
||||||
|
Ok(query) => query,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Some(Err(XPathError::Parse {
|
return Some(Err(XPathError::Parse {
|
||||||
query: query.into(),
|
query: query_string.into(),
|
||||||
error,
|
error,
|
||||||
}
|
}
|
||||||
.into()));
|
.into()));
|
||||||
|
|
@ -40,57 +40,46 @@ impl Extractor for XPathExtractor<'_> {
|
||||||
Ok(content) => content,
|
Ok(content) => content,
|
||||||
Err(error) => return Some(Err(XPathError::ReadFile(error).into())),
|
Err(error) => return Some(Err(XPathError::ReadFile(error).into())),
|
||||||
};
|
};
|
||||||
let xml = match xmlparse(RNode::new_document(), content, None) {
|
let mut documents = Documents::new();
|
||||||
|
let xml = match documents.add_string_without_uri(content) {
|
||||||
Ok(xml) => xml,
|
Ok(xml) => xml,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Some(Err(XPathError::ParseXml {
|
return Some(Err(XPathError::ParseXml {
|
||||||
error,
|
|
||||||
path: self.file.path.clone().into(),
|
path: self.file.path.clone().into(),
|
||||||
|
error,
|
||||||
}
|
}
|
||||||
.into()));
|
.into()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut static_context = StaticContextBuilder::new()
|
let result = match query.execute(&mut documents, xml) {
|
||||||
.message(|_| Ok(()))
|
Ok(result) => result,
|
||||||
.fetcher(|_| Ok(String::new()))
|
|
||||||
.parser(|_| unreachable!())
|
|
||||||
.build();
|
|
||||||
let context = ContextBuilder::new().context(vec![Item::Node(xml)]).build();
|
|
||||||
let sequence = match context.dispatch(&mut static_context, &transform) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Some(Err(XPathError::MatchError {
|
return Some(Err(XPathError::MatchError {
|
||||||
|
query: query_string.into(),
|
||||||
error,
|
error,
|
||||||
query: query.into(),
|
|
||||||
}
|
}
|
||||||
.into()));
|
.into()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Ok(sequence.to_xml().into()))
|
Some(Ok(result.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
enum XPathError {
|
enum XPathError {
|
||||||
#[error("Failed to parse xpath '{query}': {error:#}")]
|
#[error("Failed to parse xpath '{query}': {error:#}")]
|
||||||
Parse {
|
Parse { error: SpannedError, query: String },
|
||||||
error: XPathParseError,
|
|
||||||
query: String,
|
|
||||||
},
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ReadFile(FileError),
|
ReadFile(FileError),
|
||||||
#[error("Failed to parse xml '{}': {error:#}", path.display())]
|
#[error("Failed to parse xml '{}': {error:#}", path.display())]
|
||||||
ParseXml {
|
ParseXml {
|
||||||
error: XPathParseError,
|
error: DocumentsError,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
},
|
},
|
||||||
#[error("Failed to match xpath '{query}': {error:#}")]
|
#[error("Failed to match xpath '{query}': {error:#}")]
|
||||||
MatchError {
|
MatchError { error: SpannedError, query: String },
|
||||||
error: XPathParseError,
|
|
||||||
query: String,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -129,4 +118,20 @@ fn test_xpath() {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
"Any%",
|
||||||
|
matcher
|
||||||
|
.extract("xpath('//CategoryName/text()')")
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
"No Major Glitches",
|
||||||
|
matcher
|
||||||
|
.extract(
|
||||||
|
"xpath('//Metadata/Variables/Variable[contains(@name, \"Subcategory\")]/text()')"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue