feat: add attribute selection to nu_plugion_selector (#3519)

This allows the user to specify for example 
selector a -t href 
to downselect based on an attribute

Co-authored-by: ahkrr <alexhk@protonmail.com>
This commit is contained in:
ahkrr 2021-05-30 18:49:43 +02:00 committed by GitHub
parent a5c14ba7d4
commit 9dbb3e80fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 39 deletions

View File

@ -10,7 +10,7 @@ version = "0.31.1"
doctest = false doctest = false
[dependencies] [dependencies]
nipper = "0.1.8" nipper = "0.1.9"
nu-errors = { version = "0.31.1", path = "../nu-errors" } nu-errors = { version = "0.31.1", path = "../nu-errors" }
nu-plugin = { version = "0.31.1", path = "../nu-plugin" } nu-plugin = { version = "0.31.1", path = "../nu-plugin" }
nu-protocol = { version = "0.31.1", path = "../nu-protocol" } nu-protocol = { version = "0.31.1", path = "../nu-protocol" }

View File

@ -3,7 +3,6 @@ use nu_plugin::Plugin;
use nu_protocol::{ use nu_protocol::{
CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, SyntaxShape, UntaggedValue, Value, CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, SyntaxShape, UntaggedValue, Value,
}; };
use nu_source::TaggedItem;
use crate::{selector::begin_selector_query, Selector}; use crate::{selector::begin_selector_query, Selector};
@ -13,6 +12,12 @@ impl Plugin for Selector {
.desc("execute selector query on html/web") .desc("execute selector query on html/web")
.required("query", SyntaxShape::String, "selector query") .required("query", SyntaxShape::String, "selector query")
.switch("as_html", "return the query output as html", Some('a')) .switch("as_html", "return the query output as html", Some('a'))
.named(
"attribute",
SyntaxShape::String,
"downselect based on the given attribute",
Some('t'),
)
.filter()) .filter())
} }
@ -29,6 +34,9 @@ impl Plugin for Selector {
self.query = query.as_string()?; self.query = query.as_string()?;
self.tag = tag; self.tag = tag;
self.as_html = call_info.args.has("as_html"); self.as_html = call_info.args.has("as_html");
if call_info.args.has("attribute") {
self.attribute = call_info.args.expect_get("attribute")?.convert_to_string();
}
Ok(vec![]) Ok(vec![])
} }
@ -38,12 +46,10 @@ impl Plugin for Selector {
Value { Value {
value: UntaggedValue::Primitive(Primitive::String(s)), value: UntaggedValue::Primitive(Primitive::String(s)),
.. ..
} => Ok( } => Ok(begin_selector_query(s, &self)
begin_selector_query(s, (*self.query).tagged(&self.tag), self.as_html) .into_iter()
.into_iter() .map(ReturnSuccess::value)
.map(ReturnSuccess::value) .collect()),
.collect(),
),
Value { tag, .. } => Err(ShellError::labeled_error_with_secondary( Value { tag, .. } => Err(ShellError::labeled_error_with_secondary(
"Expected text from pipeline", "Expected text from pipeline",
"requires text input", "requires text input",

View File

@ -1,11 +1,12 @@
use nipper::Document; use nipper::Document;
use nu_protocol::{value::StringExt, Value}; use nu_protocol::{value::StringExt, Value};
use nu_source::{Tag, Tagged}; use nu_source::Tag;
pub struct Selector { pub struct Selector {
pub query: String, pub query: String,
pub tag: Tag, pub tag: Tag,
pub as_html: bool, pub as_html: bool,
pub attribute: String,
} }
impl Selector { impl Selector {
@ -14,6 +15,7 @@ impl Selector {
query: String::new(), query: String::new(),
tag: Tag::unknown(), tag: Tag::unknown(),
as_html: false, as_html: false,
attribute: String::new(),
} }
} }
} }
@ -24,42 +26,54 @@ impl Default for Selector {
} }
} }
pub fn begin_selector_query(input: String, query: Tagged<&str>, as_html: bool) -> Vec<Value> { pub fn begin_selector_query(input_html: String, selector: &Selector) -> Vec<Value> {
execute_selector_query(input, query.item.to_string(), query.tag(), as_html) match selector.attribute.is_empty() {
true => execute_selector_query(
input_html.as_str(),
selector.query.as_str(),
selector.as_html,
),
false => execute_selector_query_with_attribute(
input_html.as_str(),
selector.query.as_str(),
selector.attribute.as_str(),
),
}
} }
fn execute_selector_query( fn execute_selector_query_with_attribute(
input_string: String, input_string: &str,
query_string: String, query_string: &str,
tag: impl Into<Tag>, attribute: &str,
as_html: bool,
) -> Vec<Value> { ) -> Vec<Value> {
let _tag = tag.into(); let doc = Document::from(input_string);
let mut ret = vec![];
let doc = Document::from(&input_string);
// How to internally iterate doc.select(&query_string)
// doc.nip("tr.athing").iter().for_each(|athing| { .iter()
// let title = format!("{}", athing.select(".title a").text().to_string()); .map(|selection| {
// let href = athing selection
// .select(".storylink") .attr_or(attribute, "")
// .attr("href") .to_string()
// .unwrap() .to_string_value_create_tag()
// .to_string(); })
// let title_url = format!("{} - {}\n", title, href); .collect()
// ret.push(title_url.to_string_value_create_tag()); }
// });
if as_html { fn execute_selector_query(input_string: &str, query_string: &str, as_html: bool) -> Vec<Value> {
doc.nip(&query_string).iter().for_each(|athing| { let doc = Document::from(input_string);
ret.push(athing.html().to_string().to_string_value_create_tag());
}); match as_html {
} else { true => doc
doc.nip(&query_string).iter().for_each(|athing| { .select(&query_string)
ret.push(athing.text().to_string().to_string_value_create_tag()); .iter()
}); .map(|selection| selection.html().to_string().to_string_value_create_tag())
.collect(),
false => doc
.select(&query_string)
.iter()
.map(|selection| selection.text().to_string().to_string_value_create_tag())
.collect(),
} }
ret
} }
#[cfg(test)] #[cfg(test)]