From 9dbb3e80fefcd420b90fdcc185e66811e4df7d72 Mon Sep 17 00:00:00 2001 From: ahkrr <44893716+ahkrr@users.noreply.github.com> Date: Sun, 30 May 2021 18:49:43 +0200 Subject: [PATCH] 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 --- crates/nu_plugin_selector/Cargo.toml | 2 +- crates/nu_plugin_selector/src/nu/mod.rs | 20 +++--- crates/nu_plugin_selector/src/selector.rs | 76 ++++++++++++++--------- 3 files changed, 59 insertions(+), 39 deletions(-) diff --git a/crates/nu_plugin_selector/Cargo.toml b/crates/nu_plugin_selector/Cargo.toml index c6d36230b..7067fc147 100644 --- a/crates/nu_plugin_selector/Cargo.toml +++ b/crates/nu_plugin_selector/Cargo.toml @@ -10,7 +10,7 @@ version = "0.31.1" doctest = false [dependencies] -nipper = "0.1.8" +nipper = "0.1.9" nu-errors = { version = "0.31.1", path = "../nu-errors" } nu-plugin = { version = "0.31.1", path = "../nu-plugin" } nu-protocol = { version = "0.31.1", path = "../nu-protocol" } diff --git a/crates/nu_plugin_selector/src/nu/mod.rs b/crates/nu_plugin_selector/src/nu/mod.rs index 4999aa429..0359340b0 100644 --- a/crates/nu_plugin_selector/src/nu/mod.rs +++ b/crates/nu_plugin_selector/src/nu/mod.rs @@ -3,7 +3,6 @@ use nu_plugin::Plugin; use nu_protocol::{ CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, SyntaxShape, UntaggedValue, Value, }; -use nu_source::TaggedItem; use crate::{selector::begin_selector_query, Selector}; @@ -13,6 +12,12 @@ impl Plugin for Selector { .desc("execute selector query on html/web") .required("query", SyntaxShape::String, "selector query") .switch("as_html", "return the query output as html", Some('a')) + .named( + "attribute", + SyntaxShape::String, + "downselect based on the given attribute", + Some('t'), + ) .filter()) } @@ -29,6 +34,9 @@ impl Plugin for Selector { self.query = query.as_string()?; self.tag = tag; 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![]) } @@ -38,12 +46,10 @@ impl Plugin for Selector { Value { value: UntaggedValue::Primitive(Primitive::String(s)), .. - } => Ok( - begin_selector_query(s, (*self.query).tagged(&self.tag), self.as_html) - .into_iter() - .map(ReturnSuccess::value) - .collect(), - ), + } => Ok(begin_selector_query(s, &self) + .into_iter() + .map(ReturnSuccess::value) + .collect()), Value { tag, .. } => Err(ShellError::labeled_error_with_secondary( "Expected text from pipeline", "requires text input", diff --git a/crates/nu_plugin_selector/src/selector.rs b/crates/nu_plugin_selector/src/selector.rs index 6cd8b4566..799d4089e 100644 --- a/crates/nu_plugin_selector/src/selector.rs +++ b/crates/nu_plugin_selector/src/selector.rs @@ -1,11 +1,12 @@ use nipper::Document; use nu_protocol::{value::StringExt, Value}; -use nu_source::{Tag, Tagged}; +use nu_source::Tag; pub struct Selector { pub query: String, pub tag: Tag, pub as_html: bool, + pub attribute: String, } impl Selector { @@ -14,6 +15,7 @@ impl Selector { query: String::new(), tag: Tag::unknown(), 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 { - execute_selector_query(input, query.item.to_string(), query.tag(), as_html) +pub fn begin_selector_query(input_html: String, selector: &Selector) -> Vec { + 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( - input_string: String, - query_string: String, - tag: impl Into, - as_html: bool, +fn execute_selector_query_with_attribute( + input_string: &str, + query_string: &str, + attribute: &str, ) -> Vec { - let _tag = tag.into(); - let mut ret = vec![]; - let doc = Document::from(&input_string); + let doc = Document::from(input_string); - // How to internally iterate - // doc.nip("tr.athing").iter().for_each(|athing| { - // let title = format!("{}", athing.select(".title a").text().to_string()); - // let href = athing - // .select(".storylink") - // .attr("href") - // .unwrap() - // .to_string(); - // let title_url = format!("{} - {}\n", title, href); - // ret.push(title_url.to_string_value_create_tag()); - // }); + doc.select(&query_string) + .iter() + .map(|selection| { + selection + .attr_or(attribute, "") + .to_string() + .to_string_value_create_tag() + }) + .collect() +} - if as_html { - doc.nip(&query_string).iter().for_each(|athing| { - ret.push(athing.html().to_string().to_string_value_create_tag()); - }); - } else { - doc.nip(&query_string).iter().for_each(|athing| { - ret.push(athing.text().to_string().to_string_value_create_tag()); - }); +fn execute_selector_query(input_string: &str, query_string: &str, as_html: bool) -> Vec { + let doc = Document::from(input_string); + + match as_html { + true => doc + .select(&query_string) + .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)]