From d7af46117347632f84cda815c392614364d4a28c Mon Sep 17 00:00:00 2001 From: Reilly Wood <26268125+rgwood@users.noreply.github.com> Date: Tue, 3 Jan 2023 13:03:05 -0800 Subject: [PATCH] Delete unused files (#7668) Just tidying up a bit, deleting the unused files in `crates/old`. The files can still be accessed in source control history if anyone needs them. --- crates/old/nu_plugin_chart/Cargo.toml | 21 - crates/old/nu_plugin_chart/src/bar.rs | 119 ------ .../src/bin/nu_plugin_chart_bar.rs | 6 - .../src/bin/nu_plugin_chart_line.rs | 6 - crates/old/nu_plugin_chart/src/lib.rs | 5 - crates/old/nu_plugin_chart/src/line.rs | 144 ------- crates/old/nu_plugin_chart/src/nu.rs | 5 - crates/old/nu_plugin_chart/src/nu/bar.rs | 371 ------------------ crates/old/nu_plugin_chart/src/nu/line.rs | 369 ----------------- crates/old/nu_plugin_from_bson/Cargo.toml | 20 - .../old/nu_plugin_from_bson/src/from_bson.rs | 212 ---------- crates/old/nu_plugin_from_bson/src/lib.rs | 4 - crates/old/nu_plugin_from_bson/src/main.rs | 6 - crates/old/nu_plugin_from_bson/src/nu/mod.rs | 46 --- .../old/nu_plugin_from_bson/src/nu/tests.rs | 1 - crates/old/nu_plugin_from_mp4/Cargo.toml | 20 - crates/old/nu_plugin_from_mp4/src/from_mp4.rs | 174 -------- crates/old/nu_plugin_from_mp4/src/lib.rs | 4 - crates/old/nu_plugin_from_mp4/src/main.rs | 6 - crates/old/nu_plugin_from_mp4/src/nu/mod.rs | 46 --- crates/old/nu_plugin_from_mp4/src/nu/tests.rs | 1 - crates/old/nu_plugin_from_sqlite/Cargo.toml | 24 -- .../nu_plugin_from_sqlite/src/from_sqlite.rs | 138 ------- crates/old/nu_plugin_from_sqlite/src/lib.rs | 4 - crates/old/nu_plugin_from_sqlite/src/main.rs | 6 - .../old/nu_plugin_from_sqlite/src/nu/mod.rs | 75 ---- .../old/nu_plugin_from_sqlite/src/nu/tests.rs | 1 - crates/old/nu_plugin_s3/Cargo.toml | 20 - crates/old/nu_plugin_s3/README.md | 9 - crates/old/nu_plugin_s3/demo.png | Bin 81084 -> 0 bytes crates/old/nu_plugin_s3/src/handler.rs | 134 ------- crates/old/nu_plugin_s3/src/lib.rs | 4 - crates/old/nu_plugin_s3/src/main.rs | 6 - crates/old/nu_plugin_s3/src/nu/mod.rs | 60 --- crates/old/nu_plugin_start/Cargo.toml | 26 -- crates/old/nu_plugin_start/src/lib.rs | 4 - crates/old/nu_plugin_start/src/main.rs | 6 - crates/old/nu_plugin_start/src/nu/mod.rs | 28 -- crates/old/nu_plugin_start/src/start.rs | 260 ------------ crates/old/nu_plugin_to_bson/Cargo.toml | 23 -- crates/old/nu_plugin_to_bson/src/lib.rs | 4 - crates/old/nu_plugin_to_bson/src/main.rs | 6 - crates/old/nu_plugin_to_bson/src/nu/mod.rs | 25 -- crates/old/nu_plugin_to_bson/src/nu/tests.rs | 1 - crates/old/nu_plugin_to_bson/src/to_bson.rs | 300 -------------- crates/old/nu_plugin_to_sqlite/Cargo.toml | 24 -- crates/old/nu_plugin_to_sqlite/src/lib.rs | 4 - crates/old/nu_plugin_to_sqlite/src/main.rs | 6 - crates/old/nu_plugin_to_sqlite/src/nu/mod.rs | 25 -- .../old/nu_plugin_to_sqlite/src/nu/tests.rs | 1 - .../old/nu_plugin_to_sqlite/src/to_sqlite.rs | 172 -------- crates/old/nu_plugin_tree/Cargo.toml | 20 - crates/old/nu_plugin_tree/src/lib.rs | 4 - crates/old/nu_plugin_tree/src/main.rs | 6 - crates/old/nu_plugin_tree/src/nu/mod.rs | 19 - crates/old/nu_plugin_tree/src/tree.rs | 80 ---- 56 files changed, 3111 deletions(-) delete mode 100644 crates/old/nu_plugin_chart/Cargo.toml delete mode 100644 crates/old/nu_plugin_chart/src/bar.rs delete mode 100644 crates/old/nu_plugin_chart/src/bin/nu_plugin_chart_bar.rs delete mode 100644 crates/old/nu_plugin_chart/src/bin/nu_plugin_chart_line.rs delete mode 100644 crates/old/nu_plugin_chart/src/lib.rs delete mode 100644 crates/old/nu_plugin_chart/src/line.rs delete mode 100644 crates/old/nu_plugin_chart/src/nu.rs delete mode 100644 crates/old/nu_plugin_chart/src/nu/bar.rs delete mode 100644 crates/old/nu_plugin_chart/src/nu/line.rs delete mode 100644 crates/old/nu_plugin_from_bson/Cargo.toml delete mode 100644 crates/old/nu_plugin_from_bson/src/from_bson.rs delete mode 100644 crates/old/nu_plugin_from_bson/src/lib.rs delete mode 100644 crates/old/nu_plugin_from_bson/src/main.rs delete mode 100644 crates/old/nu_plugin_from_bson/src/nu/mod.rs delete mode 100644 crates/old/nu_plugin_from_bson/src/nu/tests.rs delete mode 100644 crates/old/nu_plugin_from_mp4/Cargo.toml delete mode 100644 crates/old/nu_plugin_from_mp4/src/from_mp4.rs delete mode 100644 crates/old/nu_plugin_from_mp4/src/lib.rs delete mode 100644 crates/old/nu_plugin_from_mp4/src/main.rs delete mode 100644 crates/old/nu_plugin_from_mp4/src/nu/mod.rs delete mode 100644 crates/old/nu_plugin_from_mp4/src/nu/tests.rs delete mode 100644 crates/old/nu_plugin_from_sqlite/Cargo.toml delete mode 100644 crates/old/nu_plugin_from_sqlite/src/from_sqlite.rs delete mode 100644 crates/old/nu_plugin_from_sqlite/src/lib.rs delete mode 100644 crates/old/nu_plugin_from_sqlite/src/main.rs delete mode 100644 crates/old/nu_plugin_from_sqlite/src/nu/mod.rs delete mode 100644 crates/old/nu_plugin_from_sqlite/src/nu/tests.rs delete mode 100644 crates/old/nu_plugin_s3/Cargo.toml delete mode 100644 crates/old/nu_plugin_s3/README.md delete mode 100644 crates/old/nu_plugin_s3/demo.png delete mode 100644 crates/old/nu_plugin_s3/src/handler.rs delete mode 100644 crates/old/nu_plugin_s3/src/lib.rs delete mode 100644 crates/old/nu_plugin_s3/src/main.rs delete mode 100644 crates/old/nu_plugin_s3/src/nu/mod.rs delete mode 100644 crates/old/nu_plugin_start/Cargo.toml delete mode 100644 crates/old/nu_plugin_start/src/lib.rs delete mode 100644 crates/old/nu_plugin_start/src/main.rs delete mode 100644 crates/old/nu_plugin_start/src/nu/mod.rs delete mode 100644 crates/old/nu_plugin_start/src/start.rs delete mode 100644 crates/old/nu_plugin_to_bson/Cargo.toml delete mode 100644 crates/old/nu_plugin_to_bson/src/lib.rs delete mode 100644 crates/old/nu_plugin_to_bson/src/main.rs delete mode 100644 crates/old/nu_plugin_to_bson/src/nu/mod.rs delete mode 100644 crates/old/nu_plugin_to_bson/src/nu/tests.rs delete mode 100644 crates/old/nu_plugin_to_bson/src/to_bson.rs delete mode 100644 crates/old/nu_plugin_to_sqlite/Cargo.toml delete mode 100644 crates/old/nu_plugin_to_sqlite/src/lib.rs delete mode 100644 crates/old/nu_plugin_to_sqlite/src/main.rs delete mode 100644 crates/old/nu_plugin_to_sqlite/src/nu/mod.rs delete mode 100644 crates/old/nu_plugin_to_sqlite/src/nu/tests.rs delete mode 100644 crates/old/nu_plugin_to_sqlite/src/to_sqlite.rs delete mode 100644 crates/old/nu_plugin_tree/Cargo.toml delete mode 100644 crates/old/nu_plugin_tree/src/lib.rs delete mode 100644 crates/old/nu_plugin_tree/src/main.rs delete mode 100644 crates/old/nu_plugin_tree/src/nu/mod.rs delete mode 100644 crates/old/nu_plugin_tree/src/tree.rs diff --git a/crates/old/nu_plugin_chart/Cargo.toml b/crates/old/nu_plugin_chart/Cargo.toml deleted file mode 100644 index 26f7f5ff62..0000000000 --- a/crates/old/nu_plugin_chart/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "A plugin to display charts" -edition = "2018" -license = "MIT" -name = "nu_plugin_chart" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -nu-data = { path="../nu-data", version = "0.73.1" } -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -nu-source = { path="../nu-source", version = "0.73.1" } -nu-value-ext = { path="../nu-value-ext", version = "0.73.1" } - -crossterm = "0.19.0" -tui = { version="0.15.0", default-features=false, features=["crossterm"] } diff --git a/crates/old/nu_plugin_chart/src/bar.rs b/crates/old/nu_plugin_chart/src/bar.rs deleted file mode 100644 index 4a77d0eca5..0000000000 --- a/crates/old/nu_plugin_chart/src/bar.rs +++ /dev/null @@ -1,119 +0,0 @@ -use nu_data::utils::Model; -use nu_errors::ShellError; - -use tui::{ - layout::{Constraint, Direction, Layout}, - style::{Color, Modifier, Style}, - widgets::BarChart, -}; - -const DEFAULT_COLOR: Color = Color::Green; - -pub struct Bar<'a> { - pub title: &'a str, - pub data: Vec<(&'a str, u64)>, - pub enhanced_graphics: bool, -} - -impl<'a> Bar<'a> { - pub fn from_model(model: &'a Model) -> Result, ShellError> { - let mut data = Vec::new(); - let mut data_points = Vec::new(); - - for percentages in model - .percentages - .table_entries() - .cloned() - .collect::>() - { - let mut percentages_collected = vec![]; - - for percentage in percentages.table_entries().cloned().collect::>() { - percentages_collected.push(percentage.as_u64()?); - } - - data_points.push(percentages_collected); - } - - let mark_in = if model.labels.y.len() <= 1 { - 0 - } else { - (model.labels.y.len() as f64 / 2.0).floor() as usize - }; - - for idx in 0..model.labels.x.len() { - let mut current = 0; - - loop { - let label = if current == mark_in { - model - .labels - .at(idx) - .ok_or_else(|| ShellError::untagged_runtime_error("Could not load data"))? - } else { - "" - }; - - let percentages_collected = data_points - .get(current) - .ok_or_else(|| ShellError::untagged_runtime_error("Could not load data"))?; - - data.push(( - label, - *percentages_collected - .get(idx) - .ok_or_else(|| ShellError::untagged_runtime_error("Could not load data"))?, - )); - - current += 1; - - if current == model.labels.y.len() { - break; - } - } - } - - Ok(Bar { - title: "Bar Chart", - data: (&data[..]).to_vec(), - enhanced_graphics: true, - }) - } - - pub fn draw(&mut self, ui: &mut tui::Terminal) -> std::io::Result<()> - where - T: tui::backend::Backend, - { - ui.draw(|f| { - let chunks = Layout::default() - .direction(Direction::Vertical) - .margin(0) - .constraints([Constraint::Percentage(100)].as_ref()) - .split(f.size()); - - let barchart = BarChart::default() - .data(&self.data) - .bar_width(9) - .bar_style(Style::default().fg(DEFAULT_COLOR)) - .value_style( - Style::default() - .bg(Color::Black) - .add_modifier(Modifier::BOLD), - ); - - f.render_widget(barchart, chunks[0]); - })?; - Ok(()) - } - - pub fn on_right(&mut self) { - let one_bar = self.data.remove(0); - self.data.push(one_bar); - } - - pub fn on_left(&mut self) { - if let Some(one_bar) = self.data.pop() { - self.data.insert(0, one_bar); - } - } -} diff --git a/crates/old/nu_plugin_chart/src/bin/nu_plugin_chart_bar.rs b/crates/old/nu_plugin_chart/src/bin/nu_plugin_chart_bar.rs deleted file mode 100644 index 20ada96668..0000000000 --- a/crates/old/nu_plugin_chart/src/bin/nu_plugin_chart_bar.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_chart::ChartBar; - -fn main() { - serve_plugin(&mut ChartBar::new()); -} diff --git a/crates/old/nu_plugin_chart/src/bin/nu_plugin_chart_line.rs b/crates/old/nu_plugin_chart/src/bin/nu_plugin_chart_line.rs deleted file mode 100644 index 82ae4a00db..0000000000 --- a/crates/old/nu_plugin_chart/src/bin/nu_plugin_chart_line.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_chart::ChartLine; - -fn main() { - serve_plugin(&mut ChartLine::new()); -} diff --git a/crates/old/nu_plugin_chart/src/lib.rs b/crates/old/nu_plugin_chart/src/lib.rs deleted file mode 100644 index 747c5bdcd4..0000000000 --- a/crates/old/nu_plugin_chart/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod bar; -mod line; -mod nu; - -pub use nu::{ChartBar, ChartLine}; diff --git a/crates/old/nu_plugin_chart/src/line.rs b/crates/old/nu_plugin_chart/src/line.rs deleted file mode 100644 index 53088a70d4..0000000000 --- a/crates/old/nu_plugin_chart/src/line.rs +++ /dev/null @@ -1,144 +0,0 @@ -use nu_data::utils::Model; -use nu_errors::ShellError; - -use tui::{ - layout::{Constraint, Direction, Layout}, - style::{Color, Modifier, Style}, - symbols, - text::Span, - widgets::{Axis, Chart, Dataset, GraphType}, -}; - -const DEFAULT_COLOR: Color = Color::Green; - -const DEFAULT_LINE_COLORS: [Color; 5] = [ - Color::Green, - Color::Cyan, - Color::Magenta, - Color::Yellow, - Color::Red, -]; - -#[derive(Debug)] -pub struct Line { - x_labels: Vec, - x_range: [f64; 2], - y_range: [f64; 2], - datasets_names: Vec, - data: Vec>, -} - -impl<'a> Line { - pub fn from_model(model: &'a Model) -> Result { - Ok(Line { - x_labels: model.labels.x.to_vec(), - x_range: [ - model.ranges.0.start.as_u64()? as f64, - model.labels.x.len() as f64, - ], - y_range: [ - model.ranges.1.start.as_u64()? as f64, - model.ranges.1.end.as_u64()? as f64, - ], - datasets_names: if model.labels.y.len() == 1 { - vec!["".to_string()] - } else { - model.labels.y.to_vec() - }, - data: model - .data - .table_entries() - .collect::>() - .iter() - .map(|subset| { - subset - .table_entries() - .enumerate() - .map(|(idx, data_point)| { - ( - idx as f64, - if let Ok(point) = data_point.as_u64() { - point as f64 - } else { - 0.0 - }, - ) - }) - .collect::>() - }) - .collect::>(), - }) - } - - pub fn draw(&mut self, ui: &mut tui::Terminal) -> std::io::Result<()> - where - T: tui::backend::Backend, - { - ui.draw(|f| { - let chunks = Layout::default() - .direction(Direction::Vertical) - .margin(1) - .constraints([Constraint::Percentage(100)].as_ref()) - .split(f.size()); - - let x_labels = self - .x_labels - .iter() - .map(move |label| { - Span::styled(label, Style::default().add_modifier(Modifier::BOLD)) - }) - .collect::>(); - - let y_labels = vec![ - Span::styled( - self.y_range[0].to_string(), - Style::default().add_modifier(Modifier::BOLD), - ), - Span::raw(((self.y_range[0] + self.y_range[1]) / 2.0).to_string()), - Span::styled( - self.y_range[1].to_string(), - Style::default().add_modifier(Modifier::BOLD), - ), - ]; - - let marker = if x_labels.len() > 60 { - symbols::Marker::Braille - } else { - symbols::Marker::Dot - }; - - let datasets = self - .data - .iter() - .enumerate() - .map(|(idx, data_series)| { - Dataset::default() - .name(&self.datasets_names[idx]) - .marker(marker) - .graph_type(GraphType::Line) - .style( - Style::default() - .fg(*DEFAULT_LINE_COLORS.get(idx).unwrap_or(&DEFAULT_COLOR)), - ) - .data(data_series) - }) - .collect(); - - let chart = Chart::new(datasets) - .x_axis( - Axis::default() - .style(Style::default().fg(Color::Gray)) - .labels(x_labels) - .bounds(self.x_range), - ) - .y_axis( - Axis::default() - .style(Style::default().fg(Color::Gray)) - .labels(y_labels) - .bounds(self.y_range), - ); - f.render_widget(chart, chunks[0]); - })?; - Ok(()) - } -} diff --git a/crates/old/nu_plugin_chart/src/nu.rs b/crates/old/nu_plugin_chart/src/nu.rs deleted file mode 100644 index 7b472a1675..0000000000 --- a/crates/old/nu_plugin_chart/src/nu.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod bar; -mod line; - -pub use bar::SubCommand as ChartBar; -pub use line::SubCommand as ChartLine; diff --git a/crates/old/nu_plugin_chart/src/nu/bar.rs b/crates/old/nu_plugin_chart/src/nu/bar.rs deleted file mode 100644 index 9d06c896ee..0000000000 --- a/crates/old/nu_plugin_chart/src/nu/bar.rs +++ /dev/null @@ -1,371 +0,0 @@ -use nu_data::utils::{report as build_report, Model}; -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{CallInfo, ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value}; -use nu_source::{Tagged, TaggedItem}; -use nu_value_ext::ValueExt; - -use crate::bar::Bar; - -use std::{ - error::Error, - io::stdout, - sync::mpsc, - thread, - time::{Duration, Instant}, -}; - -use crossterm::{ - event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode}, - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, -}; - -use tui::{backend::CrosstermBackend, Terminal}; - -enum Event { - Input(I), - Tick, -} - -pub enum Columns { - One(Tagged), - Two(Tagged, Tagged), - None, -} - -#[allow(clippy::type_complexity)] -pub struct SubCommand { - pub reduction: nu_data::utils::Reduction, - pub columns: Columns, - pub eval: Option Result + Send>>, - pub format: Option, -} - -impl Default for SubCommand { - fn default() -> Self { - Self::new() - } -} - -impl SubCommand { - pub fn new() -> SubCommand { - SubCommand { - reduction: nu_data::utils::Reduction::Count, - columns: Columns::None, - eval: None, - format: None, - } - } -} - -fn display(model: &Model) -> Result<(), Box> { - let mut app = Bar::from_model(model)?; - - enable_raw_mode()?; - - let mut stdout = stdout(); - execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; - - let backend = CrosstermBackend::new(stdout); - - let mut terminal = Terminal::new(backend)?; - - let (tx, rx) = mpsc::channel(); - - let tick_rate = Duration::from_millis(250); - thread::spawn(move || { - let mut last_tick = Instant::now(); - loop { - if event::poll(tick_rate - last_tick.elapsed()).is_ok() { - if let Ok(CEvent::Key(key)) = event::read() { - let _ = tx.send(Event::Input(key)); - } - } - if last_tick.elapsed() >= tick_rate { - let _ = tx.send(Event::Tick); - last_tick = Instant::now(); - } - } - }); - - terminal.clear()?; - - loop { - app.draw(&mut terminal)?; - - match rx.recv()? { - Event::Input(event) => match event.code { - KeyCode::Left => app.on_left(), - KeyCode::Right => app.on_right(), - KeyCode::Char('q') => { - disable_raw_mode()?; - execute!( - terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - )?; - terminal.show_cursor()?; - break; - } - _ => { - disable_raw_mode()?; - execute!( - terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - )?; - terminal.show_cursor()?; - break; - } - }, - Event::Tick => {} - } - } - - Ok(()) -} - -impl Plugin for SubCommand { - fn config(&mut self) -> Result { - Ok(Signature::build("chart bar") - .usage("Bar charts") - .switch("acc", "accumulate values", Some('a')) - .optional( - "columns", - SyntaxShape::Any, - "the columns to chart [x-axis y-axis]", - ) - .named( - "use", - SyntaxShape::ColumnPath, - "column to use for evaluation", - Some('u'), - ) - .named( - "format", - SyntaxShape::String, - "Specify date and time formatting", - Some('f'), - )) - } - - fn sink(&mut self, call_info: CallInfo, input: Vec) { - if let Some(Value { - value: UntaggedValue::Primitive(Primitive::Boolean(true)), - .. - }) = call_info.args.get("acc") - { - self.reduction = nu_data::utils::Reduction::Accumulate; - } - - let _ = self.run(call_info, input); - } -} - -impl SubCommand { - fn run(&mut self, call_info: CallInfo, input: Vec) -> Result<(), ShellError> { - let args = call_info.args; - let name = call_info.name_tag; - - self.eval = if let Some(path) = args.get("use") { - Some(evaluator(path.as_column_path()?.item)) - } else { - None - }; - - self.format = if let Some(fmt) = args.get("format") { - Some(fmt.as_string()?) - } else { - None - }; - - for arg in args.positional_iter() { - match arg { - Value { - value: UntaggedValue::Primitive(Primitive::String(column)), - tag, - } => { - let column = column.clone(); - self.columns = Columns::One(column.tagged(tag)); - } - Value { - value: UntaggedValue::Table(arguments), - tag, - } => { - if arguments.len() > 1 { - let col1 = arguments - .get(0) - .ok_or_else(|| { - ShellError::labeled_error( - "expected file and replace strings eg) [find replace]", - "missing find-replace values", - tag, - ) - })? - .as_string()? - .tagged(tag); - - let col2 = arguments - .get(1) - .ok_or_else(|| { - ShellError::labeled_error( - "expected file and replace strings eg) [find replace]", - "missing find-replace values", - tag, - ) - })? - .as_string()? - .tagged(tag); - - self.columns = Columns::Two(col1, col2); - } else { - let col1 = arguments - .get(0) - .ok_or_else(|| { - ShellError::labeled_error( - "expected file and replace strings eg) [find replace]", - "missing find-replace values", - tag, - ) - })? - .as_string()? - .tagged(tag); - - self.columns = Columns::One(col1); - } - } - _ => {} - } - } - - let data = UntaggedValue::table(&input).into_value(&name); - - match &self.columns { - Columns::Two(col1, col2) => { - let key = col1.clone(); - let fmt = self.format.clone(); - - let grouper = Box::new(move |_: usize, row: &Value| { - let key = key.clone(); - let fmt = fmt.clone(); - - match row.get_data_by_key(key.borrow_spanned()) { - Some(key) => { - if let Some(fmt) = fmt { - let callback = nu_data::utils::helpers::date_formatter(fmt); - callback(&key, "nothing".to_string()) - } else { - nu_value_ext::as_string(&key) - } - } - None => Err(ShellError::labeled_error( - "unknown column", - "unknown column", - key.tag(), - )), - } - }); - - let key = col2.clone(); - let splitter = Box::new(move |_: usize, row: &Value| { - let key = key.clone(); - - match row.get_data_by_key(key.borrow_spanned()) { - Some(key) => nu_value_ext::as_string(&key), - None => Err(ShellError::labeled_error( - "unknown column", - "unknown column", - key.tag(), - )), - } - }); - - let formatter = if self.format.is_some() { - let default = String::from("%b-%Y"); - - let string_fmt = self.format.as_ref().unwrap_or(&default); - - Some(nu_data::utils::helpers::date_formatter( - string_fmt.to_string(), - )) - } else { - None - }; - - let options = nu_data::utils::Operation { - grouper: Some(grouper), - splitter: Some(splitter), - format: &formatter, - eval: &self.eval, - reduction: &self.reduction, - }; - - let _ = display(&build_report(&data, options, &name)?); - } - Columns::One(col) => { - let key = col.clone(); - let fmt = self.format.clone(); - - let grouper = Box::new(move |_: usize, row: &Value| { - let key = key.clone(); - let fmt = fmt.clone(); - - match row.get_data_by_key(key.borrow_spanned()) { - Some(key) => { - if let Some(fmt) = fmt { - let callback = nu_data::utils::helpers::date_formatter(fmt); - callback(&key, "nothing".to_string()) - } else { - nu_value_ext::as_string(&key) - } - } - None => Err(ShellError::labeled_error( - "unknown column", - "unknown column", - key.tag(), - )), - } - }); - - let formatter = if self.format.is_some() { - let default = String::from("%b-%Y"); - - let string_fmt = self.format.as_ref().unwrap_or(&default); - - Some(nu_data::utils::helpers::date_formatter( - string_fmt.to_string(), - )) - } else { - None - }; - - let options = nu_data::utils::Operation { - grouper: Some(grouper), - splitter: None, - format: &formatter, - eval: &self.eval, - reduction: &self.reduction, - }; - - let _ = display(&build_report(&data, options, &name)?); - } - _ => {} - } - - Ok(()) - } -} - -pub fn evaluator(by: ColumnPath) -> Box Result + Send> { - Box::new(move |_: usize, value: &Value| { - let path = by.clone(); - - let eval = nu_value_ext::get_data_by_column_path(value, &path, move |_, _, error| error); - - match eval { - Ok(with_value) => Ok(with_value), - Err(reason) => Err(reason), - } - }) -} diff --git a/crates/old/nu_plugin_chart/src/nu/line.rs b/crates/old/nu_plugin_chart/src/nu/line.rs deleted file mode 100644 index 86911a77dc..0000000000 --- a/crates/old/nu_plugin_chart/src/nu/line.rs +++ /dev/null @@ -1,369 +0,0 @@ -use nu_data::utils::{report as build_report, Model}; -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{CallInfo, ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value}; -use nu_source::{Tagged, TaggedItem}; -use nu_value_ext::ValueExt; - -use crate::line::Line; - -use std::{ - error::Error, - io::stdout, - sync::mpsc, - thread, - time::{Duration, Instant}, -}; - -use crossterm::{ - event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode}, - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, -}; - -use tui::{backend::CrosstermBackend, Terminal}; - -enum Event { - Input(I), - Tick, -} - -pub enum Columns { - One(Tagged), - Two(Tagged, Tagged), - None, -} - -#[allow(clippy::type_complexity)] -pub struct SubCommand { - pub reduction: nu_data::utils::Reduction, - pub columns: Columns, - pub eval: Option Result + Send>>, - pub format: Option, -} - -impl Default for SubCommand { - fn default() -> Self { - Self::new() - } -} - -impl SubCommand { - pub fn new() -> SubCommand { - SubCommand { - reduction: nu_data::utils::Reduction::Count, - columns: Columns::None, - eval: None, - format: None, - } - } -} - -fn display(model: &Model) -> Result<(), Box> { - let mut app = Line::from_model(model)?; - - enable_raw_mode()?; - - let mut stdout = stdout(); - execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; - - let backend = CrosstermBackend::new(stdout); - - let mut terminal = Terminal::new(backend)?; - - let (tx, rx) = mpsc::channel(); - - let tick_rate = Duration::from_millis(250); - thread::spawn(move || { - let mut last_tick = Instant::now(); - loop { - if event::poll(tick_rate - last_tick.elapsed()).is_ok() { - if let Ok(CEvent::Key(key)) = event::read() { - let _ = tx.send(Event::Input(key)); - } - } - if last_tick.elapsed() >= tick_rate { - let _ = tx.send(Event::Tick); - last_tick = Instant::now(); - } - } - }); - - terminal.clear()?; - - loop { - app.draw(&mut terminal)?; - - match rx.recv()? { - Event::Input(event) => match event.code { - KeyCode::Char('q') => { - disable_raw_mode()?; - execute!( - terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - )?; - terminal.show_cursor()?; - break; - } - _ => { - disable_raw_mode()?; - execute!( - terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - )?; - terminal.show_cursor()?; - break; - } - }, - Event::Tick => {} - } - } - - Ok(()) -} - -impl Plugin for SubCommand { - fn config(&mut self) -> Result { - Ok(Signature::build("chart line") - .usage("Line charts") - .switch("acc", "accumulate values", Some('a')) - .optional( - "columns", - SyntaxShape::Any, - "the columns to chart [x-axis y-axis]", - ) - .named( - "use", - SyntaxShape::ColumnPath, - "column to use for evaluation", - Some('u'), - ) - .named( - "format", - SyntaxShape::String, - "Specify date and time formatting", - Some('f'), - )) - } - - fn sink(&mut self, call_info: CallInfo, input: Vec) { - if let Some(Value { - value: UntaggedValue::Primitive(Primitive::Boolean(true)), - .. - }) = call_info.args.get("acc") - { - self.reduction = nu_data::utils::Reduction::Accumulate; - } - - let _ = self.run(call_info, input); - } -} - -impl SubCommand { - fn run(&mut self, call_info: CallInfo, input: Vec) -> Result<(), ShellError> { - let args = call_info.args; - let name = call_info.name_tag; - - self.eval = if let Some(path) = args.get("use") { - Some(evaluator(path.as_column_path()?.item)) - } else { - None - }; - - self.format = if let Some(fmt) = args.get("format") { - Some(fmt.as_string()?) - } else { - None - }; - - for arg in args.positional_iter() { - match arg { - Value { - value: UntaggedValue::Primitive(Primitive::String(column)), - tag, - } => { - let column = column.clone(); - self.columns = Columns::One(column.tagged(tag)); - } - Value { - value: UntaggedValue::Table(arguments), - tag, - } => { - if arguments.len() > 1 { - let col1 = arguments - .get(0) - .ok_or_else(|| { - ShellError::labeled_error( - "expected file and replace strings eg) [find replace]", - "missing find-replace values", - tag, - ) - })? - .as_string()? - .tagged(tag); - - let col2 = arguments - .get(1) - .ok_or_else(|| { - ShellError::labeled_error( - "expected file and replace strings eg) [find replace]", - "missing find-replace values", - tag, - ) - })? - .as_string()? - .tagged(tag); - - self.columns = Columns::Two(col1, col2); - } else { - let col1 = arguments - .get(0) - .ok_or_else(|| { - ShellError::labeled_error( - "expected file and replace strings eg) [find replace]", - "missing find-replace values", - tag, - ) - })? - .as_string()? - .tagged(tag); - - self.columns = Columns::One(col1); - } - } - _ => {} - } - } - - let data = UntaggedValue::table(&input).into_value(&name); - - match &self.columns { - Columns::Two(col1, col2) => { - let key = col1.clone(); - let fmt = self.format.clone(); - - let grouper = Box::new(move |_: usize, row: &Value| { - let key = key.clone(); - let fmt = fmt.clone(); - - match row.get_data_by_key(key.borrow_spanned()) { - Some(key) => { - if let Some(fmt) = fmt { - let callback = nu_data::utils::helpers::date_formatter(fmt); - callback(&key, "nothing".to_string()) - } else { - nu_value_ext::as_string(&key) - } - } - None => Err(ShellError::labeled_error( - "unknown column", - "unknown column", - key.tag(), - )), - } - }); - - let key = col2.clone(); - let splitter = Box::new(move |_: usize, row: &Value| { - let key = key.clone(); - - match row.get_data_by_key(key.borrow_spanned()) { - Some(key) => nu_value_ext::as_string(&key), - None => Err(ShellError::labeled_error( - "unknown column", - "unknown column", - key.tag(), - )), - } - }); - - let formatter = if self.format.is_some() { - let default = String::from("%b-%Y"); - - let string_fmt = self.format.as_ref().unwrap_or(&default); - - Some(nu_data::utils::helpers::date_formatter( - string_fmt.to_string(), - )) - } else { - None - }; - - let options = nu_data::utils::Operation { - grouper: Some(grouper), - splitter: Some(splitter), - format: &formatter, - eval: &self.eval, - reduction: &self.reduction, - }; - - let _ = display(&build_report(&data, options, &name)?); - } - Columns::One(col) => { - let key = col.clone(); - let fmt = self.format.clone(); - - let grouper = Box::new(move |_: usize, row: &Value| { - let key = key.clone(); - let fmt = fmt.clone(); - - match row.get_data_by_key(key.borrow_spanned()) { - Some(key) => { - if let Some(fmt) = fmt { - let callback = nu_data::utils::helpers::date_formatter(fmt); - callback(&key, "nothing".to_string()) - } else { - nu_value_ext::as_string(&key) - } - } - None => Err(ShellError::labeled_error( - "unknown column", - "unknown column", - key.tag(), - )), - } - }); - - let formatter = if self.format.is_some() { - let default = String::from("%b-%Y"); - - let string_fmt = self.format.as_ref().unwrap_or(&default); - - Some(nu_data::utils::helpers::date_formatter( - string_fmt.to_string(), - )) - } else { - None - }; - - let options = nu_data::utils::Operation { - grouper: Some(grouper), - splitter: None, - format: &formatter, - eval: &self.eval, - reduction: &self.reduction, - }; - - let _ = display(&build_report(&data, options, &name)?); - } - _ => {} - } - - Ok(()) - } -} - -pub fn evaluator(by: ColumnPath) -> Box Result + Send> { - Box::new(move |_: usize, value: &Value| { - let path = by.clone(); - - let eval = nu_value_ext::get_data_by_column_path(value, &path, move |_, _, error| error); - - match eval { - Ok(with_value) => Ok(with_value), - Err(reason) => Err(reason), - } - }) -} diff --git a/crates/old/nu_plugin_from_bson/Cargo.toml b/crates/old/nu_plugin_from_bson/Cargo.toml deleted file mode 100644 index b54e0a30cd..0000000000 --- a/crates/old/nu_plugin_from_bson/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "A converter plugin to the bson format for Nushell" -edition = "2018" -license = "MIT" -name = "nu_plugin_from_bson" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -bigdecimal = { package = "bigdecimal", version = "0.3.0", features = ["serde"] } -bson = { version = "2.0.1", features = [ "chrono-0_4" ] } -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -nu-source = { path="../nu-source", version = "0.73.1" } - -[build-dependencies] diff --git a/crates/old/nu_plugin_from_bson/src/from_bson.rs b/crates/old/nu_plugin_from_bson/src/from_bson.rs deleted file mode 100644 index 115c52492e..0000000000 --- a/crates/old/nu_plugin_from_bson/src/from_bson.rs +++ /dev/null @@ -1,212 +0,0 @@ -use bigdecimal::BigDecimal; -use bson::{spec::BinarySubtype, Bson}; -use nu_errors::{ExpectedRange, ShellError}; -use nu_protocol::{Primitive, ReturnSuccess, ReturnValue, TaggedDictBuilder, UntaggedValue, Value}; -use nu_source::{SpannedItem, Tag}; -use std::str::FromStr; - -#[derive(Default)] -pub struct FromBson { - pub state: Vec, - pub name_tag: Tag, -} - -impl FromBson { - pub fn new() -> FromBson { - FromBson { - state: vec![], - name_tag: Tag::unknown(), - } - } -} - -fn bson_array(input: &[Bson], tag: Tag) -> Result, ShellError> { - let mut out = vec![]; - - for value in input { - out.push(convert_bson_value_to_nu_value(value, &tag)?); - } - - Ok(out) -} - -fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Result { - let tag = tag.into(); - let span = tag.span; - - Ok(match v { - Bson::Double(n) => UntaggedValue::Primitive(Primitive::from(*n)).into_value(&tag), - Bson::String(s) => { - UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(&tag) - } - Bson::Array(a) => UntaggedValue::Table(bson_array(a, tag.clone())?).into_value(&tag), - Bson::Document(doc) => { - let mut collected = TaggedDictBuilder::new(tag.clone()); - for (k, v) in doc { - collected.insert_value(k.clone(), convert_bson_value_to_nu_value(v, &tag)?); - } - - collected.into_value() - } - Bson::Boolean(b) => UntaggedValue::Primitive(Primitive::Boolean(*b)).into_value(&tag), - Bson::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag), - Bson::RegularExpression(regx) => { - let mut collected = TaggedDictBuilder::new(tag.clone()); - collected.insert_value( - "$regex".to_string(), - UntaggedValue::Primitive(Primitive::String(String::from(®x.pattern))) - .into_value(&tag), - ); - collected.insert_value( - "$options".to_string(), - UntaggedValue::Primitive(Primitive::String(String::from(®x.options))) - .into_value(&tag), - ); - collected.into_value() - } - Bson::Int32(n) => UntaggedValue::int(*n).into_value(&tag), - Bson::Int64(n) => UntaggedValue::int(*n).into_value(&tag), - Bson::Decimal128(n) => { - // TODO: this really isn't great, and we should update this to do a higher - // fidelity translation - let decimal = BigDecimal::from_str(&n.to_string()).map_err(|_| { - ShellError::range_error( - ExpectedRange::BigDecimal, - &n.spanned(span), - "converting BSON Decimal128 to BigDecimal".to_owned(), - ) - })?; - UntaggedValue::Primitive(Primitive::Decimal(decimal)).into_value(&tag) - } - Bson::JavaScriptCode(js) => { - let mut collected = TaggedDictBuilder::new(tag.clone()); - collected.insert_value( - "$javascript".to_string(), - UntaggedValue::Primitive(Primitive::String(String::from(js))).into_value(&tag), - ); - collected.into_value() - } - Bson::JavaScriptCodeWithScope(js) => { - let mut collected = TaggedDictBuilder::new(tag.clone()); - collected.insert_value( - "$javascript".to_string(), - UntaggedValue::Primitive(Primitive::String(String::from(&js.code))) - .into_value(&tag), - ); - collected.insert_value( - "$scope".to_string(), - convert_bson_value_to_nu_value(&Bson::Document(js.scope.to_owned()), tag)?, - ); - collected.into_value() - } - Bson::Timestamp(ts) => { - let mut collected = TaggedDictBuilder::new(tag.clone()); - collected.insert_value( - "$timestamp".to_string(), - UntaggedValue::int(ts.time).into_value(&tag), - ); - collected.into_value() - } - Bson::Binary(binary) => { - let mut collected = TaggedDictBuilder::new(tag.clone()); - collected.insert_value( - "$binary_subtype".to_string(), - match binary.subtype { - BinarySubtype::UserDefined(u) => UntaggedValue::int(u), - _ => UntaggedValue::Primitive(Primitive::String(binary_subtype_to_string( - binary.subtype, - ))), - } - .into_value(&tag), - ); - collected.insert_value( - "$binary".to_string(), - UntaggedValue::Primitive(Primitive::Binary(binary.bytes.to_owned())) - .into_value(&tag), - ); - collected.into_value() - } - Bson::ObjectId(obj_id) => { - let mut collected = TaggedDictBuilder::new(tag.clone()); - collected.insert_value( - "$object_id".to_string(), - UntaggedValue::Primitive(Primitive::String(obj_id.to_hex())).into_value(&tag), - ); - collected.into_value() - } - Bson::DateTime(dt) => { - UntaggedValue::Primitive(Primitive::Date(dt.to_chrono().into())).into_value(&tag) - } - Bson::Symbol(s) => { - let mut collected = TaggedDictBuilder::new(tag.clone()); - collected.insert_value( - "$symbol".to_string(), - UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(&tag), - ); - collected.into_value() - } - Bson::Undefined | Bson::MaxKey | Bson::MinKey | Bson::DbPointer(_) => { - // TODO Impelmenting Bson::Undefined, Bson::MaxKey, Bson::MinKey and Bson::DbPointer - // These Variants weren't present in the previous version. - TaggedDictBuilder::new(tag).into_value() - } - }) -} - -fn binary_subtype_to_string(bst: BinarySubtype) -> String { - match bst { - BinarySubtype::Generic => "generic", - BinarySubtype::Function => "function", - BinarySubtype::BinaryOld => "binary_old", - BinarySubtype::UuidOld => "uuid_old", - BinarySubtype::Uuid => "uuid", - BinarySubtype::Md5 => "md5", - _ => unreachable!(), - } - .to_string() -} - -#[derive(Debug)] -struct BytesReader { - pos: usize, - inner: Vec, -} - -impl BytesReader { - fn new(bytes: Vec) -> BytesReader { - BytesReader { - pos: 0, - inner: bytes, - } - } -} - -impl std::io::Read for BytesReader { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let src: &mut &[u8] = &mut self.inner[self.pos..].as_ref(); - let diff = src.read(buf)?; - self.pos += diff; - Ok(diff) - } -} - -pub fn from_bson_bytes_to_value(bytes: Vec, tag: impl Into) -> Result { - let mut docs = Vec::new(); - let mut b_reader = BytesReader::new(bytes); - while let Ok(v) = bson::de::from_reader(&mut b_reader) { - docs.push(Bson::Document(v)); - } - - convert_bson_value_to_nu_value(&Bson::Array(docs), tag) -} - -pub fn from_bson(bytes: Vec, name_tag: Tag) -> Result, ShellError> { - match from_bson_bytes_to_value(bytes, name_tag.clone()) { - Ok(x) => Ok(vec![ReturnSuccess::value(x)]), - Err(_) => Err(ShellError::labeled_error( - "Could not parse as BSON", - "input cannot be parsed as BSON", - name_tag, - )), - } -} diff --git a/crates/old/nu_plugin_from_bson/src/lib.rs b/crates/old/nu_plugin_from_bson/src/lib.rs deleted file mode 100644 index c037ee0289..0000000000 --- a/crates/old/nu_plugin_from_bson/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod from_bson; -mod nu; - -pub use from_bson::FromBson; diff --git a/crates/old/nu_plugin_from_bson/src/main.rs b/crates/old/nu_plugin_from_bson/src/main.rs deleted file mode 100644 index bec813d017..0000000000 --- a/crates/old/nu_plugin_from_bson/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_from_bson::FromBson; - -fn main() { - serve_plugin(&mut FromBson::new()) -} diff --git a/crates/old/nu_plugin_from_bson/src/nu/mod.rs b/crates/old/nu_plugin_from_bson/src/nu/mod.rs deleted file mode 100644 index a9f8bd496e..0000000000 --- a/crates/old/nu_plugin_from_bson/src/nu/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::FromBson; -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{CallInfo, Primitive, ReturnValue, Signature, UntaggedValue, Value}; -use nu_source::Tag; - -impl Plugin for FromBson { - fn config(&mut self) -> Result { - Ok(Signature::build("from bson") - .usage("Convert from .bson binary into table") - .filter()) - } - - fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { - self.name_tag = call_info.name_tag; - Ok(vec![]) - } - - fn filter(&mut self, input: Value) -> Result, ShellError> { - match input { - Value { - value: UntaggedValue::Primitive(Primitive::Binary(b)), - .. - } => { - self.state.extend_from_slice(&b); - } - Value { tag, .. } => { - return Err(ShellError::labeled_error_with_secondary( - "Expected binary from pipeline", - "requires binary input", - self.name_tag.clone(), - "value originates from here", - tag, - )); - } - } - Ok(vec![]) - } - - fn end_filter(&mut self) -> Result, ShellError> { - crate::from_bson::from_bson(self.state.clone(), Tag::unknown()) - } -} diff --git a/crates/old/nu_plugin_from_bson/src/nu/tests.rs b/crates/old/nu_plugin_from_bson/src/nu/tests.rs deleted file mode 100644 index d14c19bee3..0000000000 --- a/crates/old/nu_plugin_from_bson/src/nu/tests.rs +++ /dev/null @@ -1 +0,0 @@ -mod integration {} diff --git a/crates/old/nu_plugin_from_mp4/Cargo.toml b/crates/old/nu_plugin_from_mp4/Cargo.toml deleted file mode 100644 index 6113fb0c48..0000000000 --- a/crates/old/nu_plugin_from_mp4/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "A converter plugin to the mp4 format for Nushell" -edition = "2018" -license = "MIT" -name = "nu_plugin_from_mp4" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -nu-source = { path="../nu-source", version = "0.73.1" } -tempfile = "3.2.0" -mp4 = "0.9.0" - -[build-dependencies] diff --git a/crates/old/nu_plugin_from_mp4/src/from_mp4.rs b/crates/old/nu_plugin_from_mp4/src/from_mp4.rs deleted file mode 100644 index 671cc29339..0000000000 --- a/crates/old/nu_plugin_from_mp4/src/from_mp4.rs +++ /dev/null @@ -1,174 +0,0 @@ -use nu_errors::ShellError; -use nu_protocol::{ReturnSuccess, ReturnValue, TaggedDictBuilder, UntaggedValue, Value}; -use nu_source::Tag; -use std::fs::File; -use std::io::Write; -use std::path::Path; - -#[derive(Default)] -pub struct FromMp4 { - pub state: Vec, - pub name_tag: Tag, -} - -impl FromMp4 { - pub fn new() -> Self { - Self { - state: vec![], - name_tag: Tag::unknown(), - } - } -} - -pub fn convert_mp4_file_to_nu_value(path: &Path, tag: Tag) -> Result { - let mp4 = mp4::read_mp4(File::open(path).expect("Could not open mp4 file to read metadata"))?; - - let mut dict = TaggedDictBuilder::new(tag.clone()); - - // Build tracks table - let mut tracks = Vec::new(); - for track in mp4.tracks().values() { - let mut curr_track_dict = TaggedDictBuilder::new(tag.clone()); - - curr_track_dict.insert_untagged("track id", UntaggedValue::int(track.track_id())); - - curr_track_dict.insert_untagged( - "track type", - match track.track_type() { - Ok(t) => UntaggedValue::string(t.to_string()), - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - curr_track_dict.insert_untagged( - "media type", - match track.media_type() { - Ok(t) => UntaggedValue::string(t.to_string()), - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - curr_track_dict.insert_untagged( - "box type", - match track.box_type() { - Ok(t) => UntaggedValue::string(t.to_string()), - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - curr_track_dict.insert_untagged("width", UntaggedValue::int(track.width())); - curr_track_dict.insert_untagged("height", UntaggedValue::int(track.height())); - curr_track_dict.insert_untagged("frame_rate", UntaggedValue::from(track.frame_rate())); - - curr_track_dict.insert_untagged( - "sample freq index", - match track.sample_freq_index() { - Ok(sfi) => UntaggedValue::string(sfi.freq().to_string()), // this is a string for formatting reasons - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - curr_track_dict.insert_untagged( - "channel config", - match track.channel_config() { - Ok(cc) => UntaggedValue::string(cc.to_string()), - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - curr_track_dict.insert_untagged("language", UntaggedValue::string(track.language())); - curr_track_dict.insert_untagged("timescale", UntaggedValue::int(track.timescale())); - curr_track_dict.insert_untagged( - "duration", - UntaggedValue::duration(track.duration().as_nanos()), - ); - curr_track_dict.insert_untagged("bitrate", UntaggedValue::int(track.bitrate())); - curr_track_dict.insert_untagged("sample count", UntaggedValue::int(track.sample_count())); - - curr_track_dict.insert_untagged( - "video profile", - match track.video_profile() { - Ok(vp) => UntaggedValue::string(vp.to_string()), - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - curr_track_dict.insert_untagged( - "audio profile", - match track.audio_profile() { - Ok(ap) => UntaggedValue::string(ap.to_string()), - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - curr_track_dict.insert_untagged( - "sequence parameter set", - match track.sequence_parameter_set() { - Ok(sps) => UntaggedValue::string(format!("{:X?}", sps)), - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - curr_track_dict.insert_untagged( - "picture parameter set", - match track.picture_parameter_set() { - Ok(pps) => UntaggedValue::string(format!("{:X?}", pps)), - Err(_) => UntaggedValue::from("Unknown"), - }, - ); - - // push curr track to tracks vec - tracks.push(curr_track_dict.into_value()); - } - - dict.insert_untagged("size", UntaggedValue::big_int(mp4.size())); - - dict.insert_untagged( - "major brand", - UntaggedValue::string(mp4.major_brand().to_string()), - ); - - dict.insert_untagged("minor version", UntaggedValue::int(mp4.minor_version())); - - dict.insert_untagged( - "compatible brands", - UntaggedValue::string(format!("{:?}", mp4.compatible_brands())), - ); - - dict.insert_untagged( - "duration", - UntaggedValue::duration(mp4.duration().as_nanos()), - ); - - dict.insert_untagged("timescale", UntaggedValue::int(mp4.timescale())); - dict.insert_untagged("is fragmented", UntaggedValue::boolean(mp4.is_fragmented())); - dict.insert_untagged("tracks", UntaggedValue::Table(tracks).into_value(&tag)); - - Ok(dict.into_value()) -} - -pub fn from_mp4_bytes_to_value(mut bytes: Vec, tag: Tag) -> Result { - let mut tempfile = tempfile::NamedTempFile::new()?; - tempfile.write_all(bytes.as_mut_slice())?; - match convert_mp4_file_to_nu_value(tempfile.path(), tag) { - Ok(value) => Ok(value), - Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e)), - } -} - -pub fn from_mp4(bytes: Vec, name_tag: Tag) -> Result, ShellError> { - match from_mp4_bytes_to_value(bytes, name_tag.clone()) { - Ok(x) => match x { - Value { - value: UntaggedValue::Table(list), - .. - } => Ok(list.into_iter().map(ReturnSuccess::value).collect()), - _ => Ok(vec![ReturnSuccess::value(x)]), - }, - Err(_) => Err(ShellError::labeled_error( - "Could not parse as MP4", - "input cannot be parsed as MP4", - &name_tag, - )), - } -} diff --git a/crates/old/nu_plugin_from_mp4/src/lib.rs b/crates/old/nu_plugin_from_mp4/src/lib.rs deleted file mode 100644 index 8de6281f24..0000000000 --- a/crates/old/nu_plugin_from_mp4/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod from_mp4; -mod nu; - -pub use from_mp4::FromMp4; diff --git a/crates/old/nu_plugin_from_mp4/src/main.rs b/crates/old/nu_plugin_from_mp4/src/main.rs deleted file mode 100644 index 539b494cf6..0000000000 --- a/crates/old/nu_plugin_from_mp4/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_from_mp4::FromMp4; - -fn main() { - serve_plugin(&mut FromMp4::new()) -} diff --git a/crates/old/nu_plugin_from_mp4/src/nu/mod.rs b/crates/old/nu_plugin_from_mp4/src/nu/mod.rs deleted file mode 100644 index 4e8a8144fe..0000000000 --- a/crates/old/nu_plugin_from_mp4/src/nu/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::FromMp4; -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{CallInfo, Primitive, ReturnValue, Signature, UntaggedValue, Value}; -use nu_source::Tag; - -impl Plugin for FromMp4 { - fn config(&mut self) -> Result { - Ok(Signature::build("from mp4") - .usage("Get meta-data of mp4 file") - .filter()) - } - - fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { - self.name_tag = call_info.name_tag; - Ok(vec![]) - } - - fn filter(&mut self, input: Value) -> Result, ShellError> { - match input { - Value { - value: UntaggedValue::Primitive(Primitive::Binary(b)), - .. - } => { - self.state.extend_from_slice(&b); - } - Value { tag, .. } => { - return Err(ShellError::labeled_error_with_secondary( - "Expected binary from pipeline", - "requires binary input", - self.name_tag.clone(), - "value originates from here", - tag, - )); - } - } - Ok(vec![]) - } - - fn end_filter(&mut self) -> Result, ShellError> { - crate::from_mp4::from_mp4(self.state.clone(), Tag::unknown()) - } -} diff --git a/crates/old/nu_plugin_from_mp4/src/nu/tests.rs b/crates/old/nu_plugin_from_mp4/src/nu/tests.rs deleted file mode 100644 index d14c19bee3..0000000000 --- a/crates/old/nu_plugin_from_mp4/src/nu/tests.rs +++ /dev/null @@ -1 +0,0 @@ -mod integration {} diff --git a/crates/old/nu_plugin_from_sqlite/Cargo.toml b/crates/old/nu_plugin_from_sqlite/Cargo.toml deleted file mode 100644 index 2535e83b95..0000000000 --- a/crates/old/nu_plugin_from_sqlite/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "A converter plugin to the bson format for Nushell" -edition = "2018" -license = "MIT" -name = "nu_plugin_from_sqlite" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -bigdecimal = { package = "bigdecimal", version = "0.3.0", features = ["serde"] } -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -nu-source = { path="../nu-source", version = "0.73.1" } -tempfile = "3.2.0" - -[dependencies.rusqlite] -features = ["bundled", "blob"] -version = "0.26.1" - -[build-dependencies] diff --git a/crates/old/nu_plugin_from_sqlite/src/from_sqlite.rs b/crates/old/nu_plugin_from_sqlite/src/from_sqlite.rs deleted file mode 100644 index da7bb00da5..0000000000 --- a/crates/old/nu_plugin_from_sqlite/src/from_sqlite.rs +++ /dev/null @@ -1,138 +0,0 @@ -use bigdecimal::FromPrimitive; -use nu_errors::ShellError; -use nu_protocol::{Primitive, ReturnSuccess, ReturnValue, TaggedDictBuilder, UntaggedValue, Value}; -use nu_source::Tag; -use rusqlite::{types::ValueRef, Connection, Row}; -use std::io::Write; -use std::path::Path; - -#[derive(Default)] -pub struct FromSqlite { - pub state: Vec, - pub name_tag: Tag, - pub tables: Vec, -} - -impl FromSqlite { - pub fn new() -> FromSqlite { - FromSqlite { - state: vec![], - name_tag: Tag::unknown(), - tables: vec![], - } - } -} - -pub fn convert_sqlite_file_to_nu_value( - path: &Path, - tag: impl Into + Clone, - tables: Vec, -) -> Result { - let conn = Connection::open(path)?; - - let mut meta_out = Vec::new(); - let mut meta_stmt = conn.prepare("select name from sqlite_master where type='table'")?; - let mut meta_rows = meta_stmt.query([])?; - - while let Some(meta_row) = meta_rows.next()? { - let table_name: String = meta_row.get(0)?; - if tables.is_empty() || tables.contains(&table_name) { - let mut meta_dict = TaggedDictBuilder::new(tag.clone()); - let mut out = Vec::new(); - let mut table_stmt = conn.prepare(&format!("select * from [{}]", table_name))?; - let mut table_rows = table_stmt.query([])?; - while let Some(table_row) = table_rows.next()? { - out.push(convert_sqlite_row_to_nu_value(table_row, tag.clone())) - } - meta_dict.insert_value( - "table_name".to_string(), - UntaggedValue::Primitive(Primitive::String(table_name)).into_value(tag.clone()), - ); - meta_dict.insert_value( - "table_values", - UntaggedValue::Table(out).into_value(tag.clone()), - ); - meta_out.push(meta_dict.into_value()); - } - } - let tag = tag.into(); - Ok(UntaggedValue::Table(meta_out).into_value(tag)) -} - -fn convert_sqlite_row_to_nu_value(row: &Row, tag: impl Into + Clone) -> Value { - let mut collected = TaggedDictBuilder::new(tag.clone()); - for (i, c) in row.as_ref().column_names().iter().enumerate() { - collected.insert_value( - c.to_string(), - convert_sqlite_value_to_nu_value(row.get_ref_unwrap(i), tag.clone()), - ); - } - collected.into_value() -} - -fn convert_sqlite_value_to_nu_value(value: ValueRef, tag: impl Into + Clone) -> Value { - match value { - ValueRef::Null => { - UntaggedValue::Primitive(Primitive::String(String::from(""))).into_value(tag) - } - ValueRef::Integer(i) => UntaggedValue::int(i).into_value(tag), - ValueRef::Real(f) => { - let f = bigdecimal::BigDecimal::from_f64(f); - let tag = tag.into(); - let span = tag.span; - match f { - Some(d) => UntaggedValue::decimal(d).into_value(tag), - None => UntaggedValue::Error(ShellError::labeled_error( - "Can not convert f64 to big decimal", - "can not convert to decimal", - span, - )) - .into_value(tag), - } - } - ValueRef::Text(s) => { - // this unwrap is safe because we know the ValueRef is Text. - UntaggedValue::Primitive(Primitive::String(String::from_utf8_lossy(s).to_string())) - .into_value(tag) - } - ValueRef::Blob(u) => UntaggedValue::binary(u.to_owned()).into_value(tag), - } -} - -pub fn from_sqlite_bytes_to_value( - mut bytes: Vec, - tag: impl Into + Clone, - tables: Vec, -) -> Result { - // FIXME: should probably write a sqlite virtual filesystem - // that will allow us to use bytes as a file to avoid this - // write out, but this will require C code. Might be - // best done as a PR to rusqlite. - let mut tempfile = tempfile::NamedTempFile::new()?; - tempfile.write_all(bytes.as_mut_slice())?; - match convert_sqlite_file_to_nu_value(tempfile.path(), tag, tables) { - Ok(value) => Ok(value), - Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e)), - } -} - -pub fn from_sqlite( - bytes: Vec, - name_tag: Tag, - tables: Vec, -) -> Result, ShellError> { - match from_sqlite_bytes_to_value(bytes, name_tag.clone(), tables) { - Ok(x) => match x { - Value { - value: UntaggedValue::Table(list), - .. - } => Ok(list.into_iter().map(ReturnSuccess::value).collect()), - _ => Ok(vec![ReturnSuccess::value(x)]), - }, - Err(_) => Err(ShellError::labeled_error( - "Could not parse as SQLite", - "input cannot be parsed as SQLite", - &name_tag, - )), - } -} diff --git a/crates/old/nu_plugin_from_sqlite/src/lib.rs b/crates/old/nu_plugin_from_sqlite/src/lib.rs deleted file mode 100644 index 62e49fb460..0000000000 --- a/crates/old/nu_plugin_from_sqlite/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod from_sqlite; -mod nu; - -pub use from_sqlite::FromSqlite; diff --git a/crates/old/nu_plugin_from_sqlite/src/main.rs b/crates/old/nu_plugin_from_sqlite/src/main.rs deleted file mode 100644 index 26147638a5..0000000000 --- a/crates/old/nu_plugin_from_sqlite/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_from_sqlite::FromSqlite; - -fn main() { - serve_plugin(&mut FromSqlite::new()) -} diff --git a/crates/old/nu_plugin_from_sqlite/src/nu/mod.rs b/crates/old/nu_plugin_from_sqlite/src/nu/mod.rs deleted file mode 100644 index 5abb6767f0..0000000000 --- a/crates/old/nu_plugin_from_sqlite/src/nu/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::FromSqlite; -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{CallInfo, Primitive, ReturnValue, Signature, SyntaxShape, UntaggedValue, Value}; -use nu_source::Tag; - -// Adapted from crates/nu-command/src/commands/dataframe/utils.rs -fn convert_columns(columns: &[Value]) -> Result, ShellError> { - let res = columns - .iter() - .map(|value| match &value.value { - UntaggedValue::Primitive(Primitive::String(s)) => Ok(s.clone()), - _ => Err(ShellError::labeled_error( - "Incorrect column format", - "Only string as column name", - &value.tag, - )), - }) - .collect::, _>>()?; - - Ok(res) -} - -impl Plugin for FromSqlite { - fn config(&mut self) -> Result { - Ok(Signature::build("from sqlite") - .named( - "tables", - SyntaxShape::Table, - "Only convert specified tables", - Some('t'), - ) - .usage("Convert from sqlite binary into table") - .filter()) - } - - fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { - self.name_tag = call_info.name_tag; - - if let Some(t) = call_info.args.get("tables") { - if let UntaggedValue::Table(columns) = t.value.clone() { - self.tables = convert_columns(columns.as_slice())?; - } - } - Ok(vec![]) - } - - fn filter(&mut self, input: Value) -> Result, ShellError> { - match input { - Value { - value: UntaggedValue::Primitive(Primitive::Binary(b)), - .. - } => { - self.state.extend_from_slice(&b); - } - Value { tag, .. } => { - return Err(ShellError::labeled_error_with_secondary( - "Expected binary from pipeline", - "requires binary input", - self.name_tag.clone(), - "value originates from here", - tag, - )); - } - } - Ok(vec![]) - } - - fn end_filter(&mut self) -> Result, ShellError> { - crate::from_sqlite::from_sqlite(self.state.clone(), Tag::unknown(), self.tables.clone()) - } -} diff --git a/crates/old/nu_plugin_from_sqlite/src/nu/tests.rs b/crates/old/nu_plugin_from_sqlite/src/nu/tests.rs deleted file mode 100644 index d14c19bee3..0000000000 --- a/crates/old/nu_plugin_from_sqlite/src/nu/tests.rs +++ /dev/null @@ -1 +0,0 @@ -mod integration {} diff --git a/crates/old/nu_plugin_s3/Cargo.toml b/crates/old/nu_plugin_s3/Cargo.toml deleted file mode 100644 index f723797fa1..0000000000 --- a/crates/old/nu_plugin_s3/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "An S3 plugin for Nushell" -edition = "2018" -license = "MIT" -name = "nu_plugin_s3" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -futures = { version="0.3.12", features=["compat", "io-compat"] } -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -nu-source = { path="../nu-source", version = "0.73.1" } -s3handler = "0.7.5" - -[build-dependencies] diff --git a/crates/old/nu_plugin_s3/README.md b/crates/old/nu_plugin_s3/README.md deleted file mode 100644 index 60ffb3320a..0000000000 --- a/crates/old/nu_plugin_s3/README.md +++ /dev/null @@ -1,9 +0,0 @@ -Nu Plugin S3 ---- - -An S3 plugin for nu shell, it can load the content of S3 objects and convert into table - -#### Snapshot -In following example, the return content from httpbin is saved before as an object in AWS S3. -![snapshot](https://raw.githubusercontent.com/yanganto/nu_plugin_s3/master/demo.png) - diff --git a/crates/old/nu_plugin_s3/demo.png b/crates/old/nu_plugin_s3/demo.png deleted file mode 100644 index 8fd2a4162d5683d5bfa072a0d1d85cdab64389b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81084 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

0e7U0M@Jkb z5DyRLiu&aPS14H}C1JhmLqo3@+AF=K%>ZEdj@be+_h~6dQ4xf)dIk??S!F)hVfCl) zIy{P}xC+vh<6Sw!B)ygXp=6FFyDrfbDB{OQ?f0E9K@qQ%UaL&}=Eu24GK zbhr_SHQ+qmUr=ShaoSA~G_LP8qBOFaUY6W!m2xYEH4iy8%Rh0OZP0NtqlLCneVH;DE=j|z2L&ZXlx%VpcKF21OxDEM zqtILa3(2-8o`2v)Q%#cQa%zy%VNQf}qnieDKSzKBzwG3x)E_A1sZ&S9Hl;s%V1O4t z2L02P`tiAc*-6s_=jq7jm6f72Qj(It{cZ025tvuszsC*d=1^Atha2@9;2rSSIHxR6 z1CvLU*9jS%+2jmG)PJ($r%qY^uQC2U$?#YI+rJ*aF7Y?*`d>cWUq_z$Kb*KctWipJ zKoWJ|$xVoLKHmO^y6fE1(iQ4C3q8H5fq|!YvraR;In6Xxbso$~Ym?dUgU)5Pc6Rl! z{v?fxGMJh&eX+;&>!9DCQ%mQyT>O}bKEJ~)j|1u3vC82${{FO&Qy%>7vw7+i|G%8V v%TE2{H$C<7%s, - pub tag: Tag, - pub has_raw: bool, - pub config: CredentialConfig, -} - -impl Handler { - pub fn new() -> Handler { - Handler { - tag: Tag::unknown(), - config: CredentialConfig { - host: String::new(), - access_key: String::new(), - secret_key: String::new(), - user: None, - region: None, - s3_type: None, - secure: None, - }, - resource: None, - has_raw: false, - } - } - - pub fn setup(&mut self, call_info: CallInfo) -> ReturnValue { - self.resource = { - let r = call_info.args.nth(0).ok_or_else(|| { - ShellError::labeled_error( - "No obj or directory specified", - "for command", - &call_info.name_tag, - ) - })?; - Some(r.clone()) - }; - self.tag = call_info.name_tag.clone(); - self.has_raw = call_info.args.has("raw"); - - if let Some(e) = call_info.args.get("endpoint") { - self.config.host = e.as_string()? - } else { - return Err(ShellError::labeled_error( - "No endpoint provided", - "for command", - &call_info.name_tag, - )); - } - - if let Some(access_key) = call_info.args.get("access-key") { - self.config.access_key = access_key.as_string()? - } else { - return Err(ShellError::labeled_error( - "No access key provided", - "for command", - &call_info.name_tag, - )); - } - - if let Some(secret_key) = call_info.args.get("secret-key") { - self.config.secret_key = secret_key.as_string()? - } else { - return Err(ShellError::labeled_error( - "No secret key provided", - "for command", - &call_info.name_tag, - )); - } - - if let Some(region) = call_info.args.get("region") { - self.config.region = Some(region.as_string()?) - } - - ReturnSuccess::value(UntaggedValue::nothing().into_untagged_value()) - } -} - -impl Default for Handler { - fn default() -> Self { - Self::new() - } -} - -pub async fn s3_helper(resource: &Value, has_raw: bool, config: &CredentialConfig) -> ReturnValue { - let resource_str = resource.as_string()?; - let mut handler = S3Handler::from(config); - let (output, content_type) = handler - .cat(&resource_str) - .map_err(|e| ShellError::unexpected(e.to_string()))?; - - let extension = if has_raw { - None - } else { - fn get_accept_ext(s: String) -> Option { - if s.contains("json") { - Some("json".to_string()) - } else if s.contains("xml") { - Some("xml".to_string()) - } else if s.contains("svg") { - Some("svg".to_string()) - } else if s.contains("html") { - Some("html".to_string()) - } else { - None - } - } - // If the extension could not provide when uploading, - // try to use the resource extension. - content_type.and_then(get_accept_ext).or_else(|| { - resource_str - .split('.') - .last() - .map(String::from) - .and_then(get_accept_ext) - }) - }; - - if let Some(e) = extension { - Ok(ReturnSuccess::Action(CommandAction::AutoConvert( - UntaggedValue::string(output).into_value(Tag { - span: resource.tag.span, - anchor: Some(AnchorLocation::Url(resource_str)), - }), - e, - ))) - } else { - ReturnSuccess::value(UntaggedValue::string(output)) - } -} diff --git a/crates/old/nu_plugin_s3/src/lib.rs b/crates/old/nu_plugin_s3/src/lib.rs deleted file mode 100644 index c906216399..0000000000 --- a/crates/old/nu_plugin_s3/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod handler; -mod nu; - -pub use handler::Handler; diff --git a/crates/old/nu_plugin_s3/src/main.rs b/crates/old/nu_plugin_s3/src/main.rs deleted file mode 100644 index e3d0a58412..0000000000 --- a/crates/old/nu_plugin_s3/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_s3::handler; - -fn main() { - serve_plugin(&mut handler::Handler::new()) -} diff --git a/crates/old/nu_plugin_s3/src/nu/mod.rs b/crates/old/nu_plugin_s3/src/nu/mod.rs deleted file mode 100644 index f42c98e6c7..0000000000 --- a/crates/old/nu_plugin_s3/src/nu/mod.rs +++ /dev/null @@ -1,60 +0,0 @@ -use futures::executor::block_on; -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{CallInfo, ReturnValue, Signature, SyntaxShape}; - -use crate::handler; -use crate::handler::s3_helper; - -impl Plugin for handler::Handler { - fn config(&mut self) -> Result { - Ok(Signature::build("s3") - .usage("Load S3 resource into a cell, convert to table if possible (avoid by appending '--raw' or '-R')") - .required( - "RESOURCE", - SyntaxShape::String, - "the RESOURCE to fetch the contents from", - ) - .named( - "endpoint", - SyntaxShape::Any, - "the endpoint info for the S3 resource, i.g., s3.ap-northeast-1.amazonaws.com or 10.1.1.1", - Some('e'), - ) - .named( - "access-key", - SyntaxShape::Any, - "the access key when authenticating", - Some('a'), - ) - .named( - "secret-key", - SyntaxShape::Any, - "the secret key when authenticating", - Some('s'), - ) - .named( - "region", - SyntaxShape::Any, - "the region of the resource, default will use us-east-1", - Some('r'), - ) - .switch("raw", "fetch contents as text rather than a table", Some('R')) - .filter()) - } - - fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - self.setup(callinfo)?; - Ok(vec![block_on(s3_helper( - &self.resource.clone().ok_or_else(|| { - ShellError::labeled_error( - "internal error: resource not set", - "resource not set", - &self.tag, - ) - })?, - self.has_raw, - &self.config, - ))]) - } -} diff --git a/crates/old/nu_plugin_start/Cargo.toml b/crates/old/nu_plugin_start/Cargo.toml deleted file mode 100644 index 453dd6c76e..0000000000 --- a/crates/old/nu_plugin_start/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "A plugin to open files/URLs directly from Nushell" -edition = "2018" -license = "MIT" -name = "nu_plugin_start" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -glob = "0.3.0" -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -nu-source = { path="../nu-source", version = "0.73.1" } -url = "2.2.0" -webbrowser = "0.5.5" - -[target.'cfg(windows)'.dependencies] -open = "1.4.0" - -[build-dependencies] -nu-errors = { version = "0.73.1", path="../nu-errors" } -nu-source = { version = "0.73.1", path="../nu-source" } diff --git a/crates/old/nu_plugin_start/src/lib.rs b/crates/old/nu_plugin_start/src/lib.rs deleted file mode 100644 index de341633d3..0000000000 --- a/crates/old/nu_plugin_start/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod nu; -mod start; - -pub use start::Start; diff --git a/crates/old/nu_plugin_start/src/main.rs b/crates/old/nu_plugin_start/src/main.rs deleted file mode 100644 index 43ef421b8e..0000000000 --- a/crates/old/nu_plugin_start/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_start::Start; - -fn main() { - serve_plugin(&mut Start::new()); -} diff --git a/crates/old/nu_plugin_start/src/nu/mod.rs b/crates/old/nu_plugin_start/src/nu/mod.rs deleted file mode 100644 index b7f1849a07..0000000000 --- a/crates/old/nu_plugin_start/src/nu/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{CallInfo, ReturnValue, Signature, SyntaxShape}; - -use crate::start::Start; - -impl Plugin for Start { - fn config(&mut self) -> Result { - Ok(Signature::build("start") - .usage("Opens each file/directory/URL using the default application") - .rest( - "rest", - SyntaxShape::String, - "files/urls/directories to open", - ) - .named( - "application", - SyntaxShape::String, - "Specifies the application used for opening the files/directories/urls", - Some('a'), - ) - .filter()) - } - fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { - self.parse(call_info)?; - self.exec().map(|_| Vec::new()) - } -} diff --git a/crates/old/nu_plugin_start/src/start.rs b/crates/old/nu_plugin_start/src/start.rs deleted file mode 100644 index cae6bebe03..0000000000 --- a/crates/old/nu_plugin_start/src/start.rs +++ /dev/null @@ -1,260 +0,0 @@ -use nu_errors::ShellError; -use nu_protocol::{CallInfo, Value}; -use nu_source::{Tag, Tagged, TaggedItem}; -use std::path::Path; -#[cfg(not(target_os = "windows"))] -use std::process::{Command, Stdio}; - -#[derive(Default)] -pub struct Start { - pub tag: Tag, - pub filenames: Vec>, - pub application: Option, -} - -impl Start { - pub fn new() -> Start { - Start { - tag: Tag::unknown(), - filenames: vec![], - application: None, - } - } - - pub fn parse(&mut self, call_info: CallInfo) -> Result<(), ShellError> { - self.tag = call_info.name_tag.clone(); - self.parse_input_parameters(&call_info)?; - self.parse_application(&call_info); - Ok(()) - } - - fn add_filename(&mut self, filename: Tagged) -> Result<(), ShellError> { - if Path::new(&filename.item).exists() || url::Url::parse(&filename.item).is_ok() { - self.filenames.push(filename); - Ok(()) - } else { - Err(ShellError::labeled_error( - format!("The file '{}' does not exist", filename.item), - "doesn't exist", - filename.tag, - )) - } - } - - fn glob_to_values(&self, value: &Value) -> Result>, ShellError> { - let mut result = vec![]; - match nu_glob::glob(&value.as_string()?) { - Ok(paths) => { - for path_result in paths { - match path_result { - Ok(path) => result - .push(path.to_string_lossy().to_string().tagged(value.tag.clone())), - Err(glob_error) => { - return Err(ShellError::labeled_error( - glob_error.to_string(), - "glob error", - value.tag.clone(), - )); - } - } - } - } - Err(pattern_error) => { - return Err(ShellError::labeled_error( - pattern_error.to_string(), - "invalid pattern", - value.tag.clone(), - )) - } - } - - Ok(result) - } - - fn parse_input_parameters(&mut self, call_info: &CallInfo) -> Result<(), ShellError> { - let candidates = match &call_info.args.positional { - Some(values) => { - let mut result = vec![]; - - for value in values { - let val_str = value.as_string(); - match val_str { - Ok(s) => { - if s.to_ascii_lowercase().starts_with("http") - || s.to_ascii_lowercase().starts_with("https") - { - if webbrowser::open(&s).is_ok() { - result.push("http/web".to_string().tagged_unknown()) - } else { - return Err(ShellError::labeled_error( - &format!("error opening {}", &s), - "error opening url", - self.tag.span, - )); - } - } else { - let res = self.glob_to_values(value)?; - result.extend(res); - } - } - Err(e) => { - return Err(ShellError::labeled_error( - e.to_string(), - "no input given", - self.tag.span, - )); - } - } - } - - if result.is_empty() { - return Err(ShellError::labeled_error( - "No input given", - "no input given", - self.tag.span, - )); - } - result - } - None => { - return Err(ShellError::labeled_error( - "No input given", - "no input given", - self.tag.span, - )) - } - }; - - for candidate in candidates { - if !candidate.contains("http/web") { - self.add_filename(candidate)?; - } - } - - Ok(()) - } - - fn parse_application(&mut self, call_info: &CallInfo) { - self.application = if let Some(app) = call_info.args.get("application") { - match app.as_string() { - Ok(name) => Some(name), - Err(_) => None, - } - } else { - None - }; - } - - #[cfg(target_os = "macos")] - pub fn exec(&mut self) -> Result<(), ShellError> { - let mut args = vec![]; - args.append( - &mut self - .filenames - .iter() - .map(|x| x.item.clone()) - .collect::>(), - ); - - if let Some(app_name) = &self.application { - args.append(&mut vec![String::from("-a"), app_name.to_string()]); - } - exec_cmd("open", &args, self.tag.clone()) - } - - #[cfg(target_os = "windows")] - pub fn exec(&mut self) -> Result<(), ShellError> { - if let Some(app_name) = &self.application { - for file in &self.filenames { - match open::with(&file.item, app_name) { - Ok(_) => continue, - Err(_) => { - return Err(ShellError::labeled_error( - "Failed to open file with specified application", - "can't open with specified application", - file.tag.span, - )) - } - } - } - } else { - for file in &self.filenames { - match open::that(&file.item) { - Ok(_) => continue, - Err(_) => { - return Err(ShellError::labeled_error( - "Failed to open file with default application", - "can't open with default application", - file.tag.span, - )) - } - } - } - } - Ok(()) - } - - #[cfg(not(any(target_os = "windows", target_os = "macos")))] - pub fn exec(&mut self) -> Result<(), ShellError> { - let mut args = vec![]; - args.append( - &mut self - .filenames - .iter() - .map(|x| x.item.clone()) - .collect::>(), - ); - - if let Some(app_name) = &self.application { - exec_cmd(app_name, &args, self.tag.clone()) - } else { - for cmd in ["xdg-open", "gnome-open", "kde-open", "wslview"] { - if exec_cmd(cmd, &args, self.tag.clone()).is_err() { - continue; - } else { - return Ok(()); - } - } - Err(ShellError::labeled_error( - "Failed to open file(s) with xdg-open. gnome-open, kde-open, and wslview", - "failed to open xdg-open. gnome-open, kde-open, and wslview", - self.tag.span, - )) - } - } -} - -#[cfg(not(target_os = "windows"))] -fn exec_cmd(cmd: &str, args: &[String], tag: Tag) -> Result<(), ShellError> { - if args.is_empty() { - return Err(ShellError::labeled_error( - "No file(s) or application provided", - "no file(s) or application provided", - tag, - )); - } - let status = match Command::new(cmd) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .args(args) - .status() - { - Ok(exit_code) => exit_code, - Err(_) => { - return Err(ShellError::labeled_error( - "Failed to run native open syscall", - "failed to run native open call", - tag, - )) - } - }; - if status.success() { - Ok(()) - } else { - Err(ShellError::labeled_error( - "Failed to run start. Hint: The file(s)/application may not exist", - "failed to run", - tag, - )) - } -} diff --git a/crates/old/nu_plugin_to_bson/Cargo.toml b/crates/old/nu_plugin_to_bson/Cargo.toml deleted file mode 100644 index 61cf1f9231..0000000000 --- a/crates/old/nu_plugin_to_bson/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "A converter plugin to the bson format for Nushell" -edition = "2018" -license = "MIT" -name = "nu_plugin_to_bson" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -bson = { version = "2.0.1", features = [ "chrono-0_4" ] } -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -nu-source = { path="../nu-source", version = "0.73.1" } -num-traits = "0.2.14" - -[features] -dataframe = ["nu-protocol/dataframe"] - -[build-dependencies] diff --git a/crates/old/nu_plugin_to_bson/src/lib.rs b/crates/old/nu_plugin_to_bson/src/lib.rs deleted file mode 100644 index 9193d10c5e..0000000000 --- a/crates/old/nu_plugin_to_bson/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod nu; -mod to_bson; - -pub use to_bson::ToBson; diff --git a/crates/old/nu_plugin_to_bson/src/main.rs b/crates/old/nu_plugin_to_bson/src/main.rs deleted file mode 100644 index 47d3f0468c..0000000000 --- a/crates/old/nu_plugin_to_bson/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_to_bson::ToBson; - -fn main() { - serve_plugin(&mut ToBson::new()) -} diff --git a/crates/old/nu_plugin_to_bson/src/nu/mod.rs b/crates/old/nu_plugin_to_bson/src/nu/mod.rs deleted file mode 100644 index bf4c1b2535..0000000000 --- a/crates/old/nu_plugin_to_bson/src/nu/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::ToBson; -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{ReturnValue, Signature, Value}; -use nu_source::Tag; - -impl Plugin for ToBson { - fn config(&mut self) -> Result { - Ok(Signature::build("to bson") - .usage("Convert table into .bson binary") - .filter()) - } - - fn filter(&mut self, input: Value) -> Result, ShellError> { - self.state.push(input); - Ok(vec![]) - } - - fn end_filter(&mut self) -> Result, ShellError> { - Ok(crate::to_bson::to_bson(self.state.clone(), Tag::unknown())) - } -} diff --git a/crates/old/nu_plugin_to_bson/src/nu/tests.rs b/crates/old/nu_plugin_to_bson/src/nu/tests.rs deleted file mode 100644 index d14c19bee3..0000000000 --- a/crates/old/nu_plugin_to_bson/src/nu/tests.rs +++ /dev/null @@ -1 +0,0 @@ -mod integration {} diff --git a/crates/old/nu_plugin_to_bson/src/to_bson.rs b/crates/old/nu_plugin_to_bson/src/to_bson.rs deleted file mode 100644 index b4bf79c5e7..0000000000 --- a/crates/old/nu_plugin_to_bson/src/to_bson.rs +++ /dev/null @@ -1,300 +0,0 @@ -use bson::{oid::ObjectId, spec::BinarySubtype, Bson, Document}; -use nu_errors::{CoerceInto, ShellError}; -use nu_protocol::{ - Dictionary, Primitive, ReturnSuccess, ReturnValue, SpannedTypeName, UnspannedPathMember, - UntaggedValue, Value, -}; -use nu_source::{Tag, TaggedItem}; -use num_traits::ToPrimitive; -use std::convert::TryInto; - -#[derive(Default)] -pub struct ToBson { - pub state: Vec, -} - -impl ToBson { - pub fn new() -> ToBson { - ToBson { state: vec![] } - } -} - -pub fn value_to_bson_value(v: &Value) -> Result { - Ok(match &v.value { - UntaggedValue::Primitive(Primitive::Boolean(b)) => Bson::Boolean(*b), - // FIXME: What about really big decimals? - UntaggedValue::Primitive(Primitive::Filesize(decimal)) => Bson::Double( - (decimal) - .to_f64() - .expect("Unimplemented BUG: What about big decimals?"), - ), - UntaggedValue::Primitive(Primitive::Duration(i)) => Bson::String(i.to_string()), - UntaggedValue::Primitive(Primitive::Date(d)) => { - Bson::DateTime(bson::DateTime::from_chrono(*d)) - } - UntaggedValue::Primitive(Primitive::EndOfStream) => Bson::Null, - UntaggedValue::Primitive(Primitive::BeginningOfStream) => Bson::Null, - UntaggedValue::Primitive(Primitive::Decimal(d)) => { - Bson::Double(d.to_f64().ok_or_else(|| { - ShellError::labeled_error( - "Could not convert value to decimal", - "could not convert to decimal", - &v.tag, - ) - })?) - } - UntaggedValue::Primitive(Primitive::Int(i)) => Bson::Int64(*i), - UntaggedValue::Primitive(Primitive::BigInt(i)) => { - Bson::Int64(i.tagged(&v.tag).coerce_into("converting to BSON")?) - } - UntaggedValue::Primitive(Primitive::Nothing) => Bson::Null, - UntaggedValue::Primitive(Primitive::String(s)) => Bson::String(s.clone()), - UntaggedValue::Primitive(Primitive::ColumnPath(path)) => Bson::Array( - path.iter() - .map(|x| match &x.unspanned { - UnspannedPathMember::String(string) => Ok(Bson::String(string.clone())), - UnspannedPathMember::Int(int) => Ok(Bson::Int64(*int)), - }) - .collect::, ShellError>>()?, - ), - UntaggedValue::Primitive(Primitive::GlobPattern(p)) => Bson::String(p.clone()), - UntaggedValue::Primitive(Primitive::FilePath(s)) => Bson::String(s.display().to_string()), - UntaggedValue::Table(l) => Bson::Array( - l.iter() - .map(value_to_bson_value) - .collect::>()?, - ), - UntaggedValue::Block(_) | UntaggedValue::Primitive(Primitive::Range(_)) => Bson::Null, - #[cfg(feature = "dataframe")] - UntaggedValue::DataFrame(_) | UntaggedValue::FrameStruct(_) => Bson::Null, - UntaggedValue::Error(e) => return Err(e.clone()), - UntaggedValue::Primitive(Primitive::Binary(b)) => Bson::Binary(bson::Binary { - subtype: BinarySubtype::Generic, - bytes: b.clone(), - }), - UntaggedValue::Row(o) => object_value_to_bson(o)?, - // TODO Impelmenting Bson::Undefined, Bson::MaxKey, Bson::MinKey and Bson::DbPointer - // These Variants weren't present in the previous version. - }) -} - -// object_value_to_bson handles all Objects, even those that correspond to special -// types (things like regex or javascript code). -fn object_value_to_bson(o: &Dictionary) -> Result { - let mut it = o.entries.iter(); - if it.len() > 2 { - return generic_object_value_to_bson(o); - } - match it.next() { - Some((regex, tagged_regex_value)) if regex == "$regex" => match it.next() { - Some((options, tagged_opts_value)) if options == "$options" => { - let r: Result = tagged_regex_value.try_into(); - let opts: Result = tagged_opts_value.try_into(); - match (r, opts) { - (Ok(pattern), Ok(options)) => { - Ok(Bson::RegularExpression(bson::Regex { pattern, options })) - } - _ => generic_object_value_to_bson(o), - } - } - _ => generic_object_value_to_bson(o), - }, - Some((javascript, tagged_javascript_value)) if javascript == "$javascript" => { - match it.next() { - Some((scope, tagged_scope_value)) if scope == "$scope" => { - let js: Result = tagged_javascript_value.try_into(); - let s: Result<&Dictionary, _> = tagged_scope_value.try_into(); - - match (js, s) { - (Ok(code), Ok(s)) => { - if let Bson::Document(scope) = object_value_to_bson(s)? { - Ok(Bson::JavaScriptCodeWithScope( - bson::JavaScriptCodeWithScope { code, scope }, - )) - } else { - generic_object_value_to_bson(o) - } - } - _ => generic_object_value_to_bson(o), - } - } - None => { - let js: Result = tagged_javascript_value.try_into(); - - match js { - Err(_) => generic_object_value_to_bson(o), - Ok(v) => Ok(Bson::JavaScriptCode(v)), - } - } - _ => generic_object_value_to_bson(o), - } - } - Some((timestamp, tagged_timestamp_value)) if timestamp == "$timestamp" => { - let ts: Result = tagged_timestamp_value.try_into(); - if let Ok(time) = ts { - Ok(Bson::Timestamp(bson::Timestamp { - time: time as u32, - increment: Default::default(), - })) - } else { - generic_object_value_to_bson(o) - } - } - Some((binary_subtype, tagged_binary_subtype_value)) - if binary_subtype == "$binary_subtype" => - { - match it.next() { - Some((binary, tagged_bin_value)) if binary == "$binary" => { - let bst = get_binary_subtype(tagged_binary_subtype_value); - let bin: Result, _> = tagged_bin_value.try_into(); - - match (bin, bst) { - (Ok(bin), Ok(subtype)) => Ok(Bson::Binary(bson::Binary { - subtype, - bytes: bin, - })), - _ => generic_object_value_to_bson(o), - } - } - _ => generic_object_value_to_bson(o), - } - } - Some((object_id, tagged_object_id_value)) if object_id == "$object_id" => { - let obj_id: Result = tagged_object_id_value.try_into(); - - if let Ok(obj_id) = obj_id { - let obj_id = ObjectId::parse_str(&obj_id); - - if let Ok(obj_id) = obj_id { - Ok(Bson::ObjectId(obj_id)) - } else { - generic_object_value_to_bson(o) - } - } else { - generic_object_value_to_bson(o) - } - } - Some((symbol, tagged_symbol_value)) if symbol == "$symbol" => { - let sym: Result = tagged_symbol_value.try_into(); - if let Ok(sym) = sym { - Ok(Bson::Symbol(sym)) - } else { - generic_object_value_to_bson(o) - } - } - _ => generic_object_value_to_bson(o), - } -} - -fn get_binary_subtype(tagged_value: &Value) -> Result { - match &tagged_value.value { - UntaggedValue::Primitive(Primitive::String(s)) => Ok(match s.as_ref() { - "generic" => BinarySubtype::Generic, - "function" => BinarySubtype::Function, - "binary_old" => BinarySubtype::BinaryOld, - "uuid_old" => BinarySubtype::UuidOld, - "uuid" => BinarySubtype::Uuid, - "md5" => BinarySubtype::Md5, - _ => unreachable!(), - }), - UntaggedValue::Primitive(Primitive::BigInt(i)) => Ok(BinarySubtype::UserDefined( - i.tagged(&tagged_value.tag) - .coerce_into("converting to BSON binary subtype")?, - )), - _ => Err(ShellError::type_error( - "bson binary", - tagged_value.spanned_type_name(), - )), - } -} - -// generic_object_value_bson handles any Object that does not -// correspond to a special bson type (things like regex or javascript code). -fn generic_object_value_to_bson(o: &Dictionary) -> Result { - let mut doc = Document::new(); - for (k, v) in &o.entries { - doc.insert(k.clone(), value_to_bson_value(v)?); - } - Ok(Bson::Document(doc)) -} - -fn shell_encode_document(writer: &mut Vec, doc: Document, tag: Tag) -> Result<(), ShellError> { - match doc.to_writer(writer) { - Err(e) => Err(ShellError::labeled_error( - format!("Failed to encode document due to: {:?}", e), - "requires BSON-compatible document", - tag, - )), - _ => Ok(()), - } -} - -fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result, ShellError> { - let mut out = Vec::new(); - match bson { - Bson::Array(a) => { - for v in a { - match v { - Bson::Document(d) => shell_encode_document(&mut out, d, tag.clone())?, - _ => { - return Err(ShellError::labeled_error( - format!("All top level values must be Documents, got {:?}", v), - "requires BSON-compatible document", - &tag, - )) - } - } - } - } - Bson::Document(d) => shell_encode_document(&mut out, d, tag)?, - _ => { - return Err(ShellError::labeled_error( - format!("All top level values must be Documents, got {:?}", bson), - "requires BSON-compatible document", - tag, - )) - } - } - Ok(out) -} - -pub fn to_bson(input: Vec, name_tag: Tag) -> Vec { - let name_span = name_tag.span; - - let to_process_input = match input.len() { - x if x > 1 => { - let tag = input[0].tag.clone(); - vec![Value { - value: UntaggedValue::Table(input), - tag, - }] - } - 1 => input, - _ => vec![], - }; - - to_process_input - .into_iter() - .map(move |value| match value_to_bson_value(&value) { - Ok(bson_value) => { - let value_span = value.tag.span; - - match bson_value_to_bytes(bson_value, name_tag.clone()) { - Ok(x) => ReturnSuccess::value(UntaggedValue::binary(x).into_value(name_span)), - _ => Err(ShellError::labeled_error_with_secondary( - "Expected a table with BSON-compatible structure from pipeline", - "requires BSON-compatible input", - name_span, - "originates from here".to_string(), - value_span, - )), - } - } - _ => Err(ShellError::labeled_error( - "Expected a table with BSON-compatible structure from pipeline", - "requires BSON-compatible input", - &name_tag, - )), - }) - .collect() -} diff --git a/crates/old/nu_plugin_to_sqlite/Cargo.toml b/crates/old/nu_plugin_to_sqlite/Cargo.toml deleted file mode 100644 index bbb28f686a..0000000000 --- a/crates/old/nu_plugin_to_sqlite/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "A converter plugin to the SQLite format for Nushell" -edition = "2018" -license = "MIT" -name = "nu_plugin_to_sqlite" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -hex = "0.4.2" -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -nu-source = { path="../nu-source", version = "0.73.1" } -tempfile = "3.2.0" - -[dependencies.rusqlite] -features = ["bundled", "blob"] -version = "0.26.1" - -[build-dependencies] diff --git a/crates/old/nu_plugin_to_sqlite/src/lib.rs b/crates/old/nu_plugin_to_sqlite/src/lib.rs deleted file mode 100644 index 3e814f55b7..0000000000 --- a/crates/old/nu_plugin_to_sqlite/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod nu; -mod to_sqlite; - -pub use to_sqlite::ToSqlite; diff --git a/crates/old/nu_plugin_to_sqlite/src/main.rs b/crates/old/nu_plugin_to_sqlite/src/main.rs deleted file mode 100644 index 5f69232256..0000000000 --- a/crates/old/nu_plugin_to_sqlite/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_to_sqlite::ToSqlite; - -fn main() { - serve_plugin(&mut ToSqlite::new()) -} diff --git a/crates/old/nu_plugin_to_sqlite/src/nu/mod.rs b/crates/old/nu_plugin_to_sqlite/src/nu/mod.rs deleted file mode 100644 index 22670e437a..0000000000 --- a/crates/old/nu_plugin_to_sqlite/src/nu/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::ToSqlite; -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{ReturnValue, Signature, Value}; -use nu_source::Tag; - -impl Plugin for ToSqlite { - fn config(&mut self) -> Result { - Ok(Signature::build("to sqlite") - .usage("Convert table into sqlite binary") - .filter()) - } - - fn filter(&mut self, input: Value) -> Result, ShellError> { - self.state.push(input); - Ok(vec![]) - } - - fn end_filter(&mut self) -> Result, ShellError> { - crate::to_sqlite::to_sqlite(self.state.clone(), Tag::unknown()) - } -} diff --git a/crates/old/nu_plugin_to_sqlite/src/nu/tests.rs b/crates/old/nu_plugin_to_sqlite/src/nu/tests.rs deleted file mode 100644 index d14c19bee3..0000000000 --- a/crates/old/nu_plugin_to_sqlite/src/nu/tests.rs +++ /dev/null @@ -1 +0,0 @@ -mod integration {} diff --git a/crates/old/nu_plugin_to_sqlite/src/to_sqlite.rs b/crates/old/nu_plugin_to_sqlite/src/to_sqlite.rs deleted file mode 100644 index 69466338c9..0000000000 --- a/crates/old/nu_plugin_to_sqlite/src/to_sqlite.rs +++ /dev/null @@ -1,172 +0,0 @@ -use hex::encode; -use nu_errors::ShellError; -use nu_protocol::{Dictionary, Primitive, ReturnSuccess, ReturnValue, UntaggedValue, Value}; -use nu_source::Tag; -use rusqlite::Connection; -use std::io::Read; - -#[derive(Default)] -pub struct ToSqlite { - pub state: Vec, -} - -impl ToSqlite { - pub fn new() -> ToSqlite { - ToSqlite { state: vec![] } - } -} -fn comma_concat(acc: String, current: String) -> String { - if acc.is_empty() { - current - } else { - format!("{}, {}", acc, current) - } -} - -fn get_columns(rows: &[Value]) -> Result { - match &rows[0].value { - UntaggedValue::Row(d) => Ok(d - .entries - .iter() - .map(|(k, _v)| k.clone()) - .fold("".to_string(), comma_concat)), - _ => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Could not find table column names", - )), - } -} - -fn nu_value_to_sqlite_string(v: Value) -> String { - match &v.value { - UntaggedValue::Primitive(p) => match p { - Primitive::Nothing => "NULL".into(), - Primitive::BigInt(i) => i.to_string(), - Primitive::Int(i) => i.to_string(), - Primitive::Duration(i) => i.to_string(), - Primitive::Decimal(f) => f.to_string(), - Primitive::Filesize(u) => u.to_string(), - Primitive::GlobPattern(s) => format!("'{}'", s.replace("'", "''")), - Primitive::String(s) => format!("'{}'", s.replace("'", "''")), - Primitive::Boolean(true) => "1".into(), - Primitive::Boolean(_) => "0".into(), - Primitive::Date(d) => format!("'{}'", d), - Primitive::FilePath(p) => format!("'{}'", p.display().to_string().replace("'", "''")), - Primitive::Binary(u) => format!("x'{}'", encode(u)), - Primitive::BeginningOfStream - | Primitive::EndOfStream - | Primitive::ColumnPath(_) - | Primitive::Range(_) => "NULL".into(), - }, - _ => "NULL".into(), - } -} - -fn get_insert_values(rows: Vec) -> Result { - let values: Result, _> = rows - .into_iter() - .map(|value| match value.value { - UntaggedValue::Row(d) => Ok(format!( - "({})", - d.entries - .iter() - .map(|(_k, v)| nu_value_to_sqlite_string(v.clone())) - .fold("".to_string(), comma_concat) - )), - _ => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Could not find table column names", - )), - }) - .collect(); - let values = values?; - Ok(values.join(", ")) -} - -fn generate_statements(table: Dictionary) -> Result<(String, String), std::io::Error> { - let table_name = match table.entries.get("table_name") { - Some(Value { - value: UntaggedValue::Primitive(Primitive::String(table_name)), - .. - }) => table_name, - _ => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Could not find table name", - )) - } - }; - let (columns, insert_values) = match table.entries.get("table_values") { - Some(Value { - value: UntaggedValue::Table(l), - .. - }) => { - if l.is_empty() { - return Ok((String::new(), String::new())); - } - (get_columns(l), get_insert_values(l.to_vec())) - } - _ => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Could not find table values", - )) - } - }; - - let create = format!("create table {}({})", table_name, columns?); - let insert = format!("insert into {} values {}", table_name, insert_values?); - Ok((create, insert)) -} - -fn sqlite_input_stream_to_bytes(values: Vec) -> Result { - // FIXME: should probably write a sqlite virtual filesystem - // that will allow us to use bytes as a file to avoid this - // write out, but this will require C code. Might be - // best done as a PR to rusqlite. - let mut tempfile = tempfile::NamedTempFile::new()?; - let conn = match Connection::open(tempfile.path()) { - Ok(conn) => conn, - Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::Other, e)), - }; - let tag = values[0].tag.clone(); - for value in values { - match &value.value { - UntaggedValue::Row(d) => { - let (create, insert) = generate_statements(d.to_owned())?; - if create.is_empty() { - continue; - } - match conn - .execute(&create, []) - .and_then(|_| conn.execute(&insert, [])) - { - Ok(_) => (), - Err(e) => { - return Err(std::io::Error::new(std::io::ErrorKind::Other, e)); - } - } - } - other => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("Expected row, found {:?}", other), - )) - } - } - } - let mut out = Vec::new(); - tempfile.read_to_end(&mut out)?; - Ok(UntaggedValue::binary(out).into_value(tag)) -} - -pub fn to_sqlite(input: Vec, name_tag: Tag) -> Result, ShellError> { - match sqlite_input_stream_to_bytes(input) { - Ok(out) => Ok(vec![ReturnSuccess::value(out)]), - _ => Err(ShellError::labeled_error( - "Expected a table with SQLite-compatible structure from pipeline", - "requires SQLite-compatible input", - name_tag, - )), - } -} diff --git a/crates/old/nu_plugin_tree/Cargo.toml b/crates/old/nu_plugin_tree/Cargo.toml deleted file mode 100644 index ed812d6269..0000000000 --- a/crates/old/nu_plugin_tree/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -authors = ["The Nushell Project Developers"] -description = "Tree viewer plugin for Nushell" -edition = "2018" -license = "MIT" -name = "nu_plugin_tree" -version = "0.73.1" - -[lib] -doctest = false - -[dependencies] -derive-new = "0.5.8" -nu-errors = { path="../nu-errors", version = "0.73.1" } -nu-plugin = { path="../nu-plugin", version = "0.73.1" } -nu-protocol = { path="../nu-protocol", version = "0.73.1" } -ptree = { version = "0.4.0", default-features = false } - - -[build-dependencies] diff --git a/crates/old/nu_plugin_tree/src/lib.rs b/crates/old/nu_plugin_tree/src/lib.rs deleted file mode 100644 index d71f024ce6..0000000000 --- a/crates/old/nu_plugin_tree/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod nu; -mod tree; - -pub use tree::TreeViewer; diff --git a/crates/old/nu_plugin_tree/src/main.rs b/crates/old/nu_plugin_tree/src/main.rs deleted file mode 100644 index 7ba6d9ea58..0000000000 --- a/crates/old/nu_plugin_tree/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use nu_plugin::serve_plugin; -use nu_plugin_tree::TreeViewer; - -fn main() { - serve_plugin(&mut TreeViewer); -} diff --git a/crates/old/nu_plugin_tree/src/nu/mod.rs b/crates/old/nu_plugin_tree/src/nu/mod.rs deleted file mode 100644 index 3dda837892..0000000000 --- a/crates/old/nu_plugin_tree/src/nu/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -use nu_errors::ShellError; -use nu_plugin::Plugin; -use nu_protocol::{CallInfo, Signature, Value}; - -use crate::tree::TreeView; -use crate::TreeViewer; - -impl Plugin for TreeViewer { - fn config(&mut self) -> Result { - Ok(Signature::build("tree").usage("View the contents of the pipeline as a tree.")) - } - - fn sink(&mut self, _call_info: CallInfo, input: Vec) { - for i in &input { - let view = TreeView::from_value(i); - let _ = view.render_view(); - } - } -} diff --git a/crates/old/nu_plugin_tree/src/tree.rs b/crates/old/nu_plugin_tree/src/tree.rs deleted file mode 100644 index 7a84f8cad1..0000000000 --- a/crates/old/nu_plugin_tree/src/tree.rs +++ /dev/null @@ -1,80 +0,0 @@ -use derive_new::new; -use nu_errors::ShellError; -use nu_protocol::{format_primitive, UntaggedValue, Value}; -use ptree::item::StringItem; -use ptree::output::print_tree_with; -use ptree::print_config::PrintConfig; -use ptree::style::{Color, Style}; -use ptree::TreeBuilder; - -pub struct TreeViewer; -#[derive(new)] -pub struct TreeView { - tree: StringItem, -} - -impl TreeView { - fn from_value_helper(value: &UntaggedValue, mut builder: &mut TreeBuilder) { - match value { - UntaggedValue::Primitive(p) => { - let _ = builder.add_empty_child(format_primitive(p, None)); - } - UntaggedValue::Row(o) => { - for (k, v) in &o.entries { - builder = builder.begin_child(k.clone()); - Self::from_value_helper(v, builder); - builder = builder.end_child(); - } - } - UntaggedValue::Table(l) => { - for elem in l { - Self::from_value_helper(elem, builder); - } - } - _ => {} - } - } - - pub fn from_value(value: &Value) -> TreeView { - let descs = value.data_descriptors(); - - let mut tree = TreeBuilder::new("".to_string()); - let mut builder = &mut tree; - - for desc in descs { - let value = match &value.value { - UntaggedValue::Row(d) => d.get_data(&desc).borrow().clone(), - _ => value.clone(), - }; - builder = builder.begin_child(desc.clone()); - Self::from_value_helper(&value, builder); - builder = builder.end_child(); - //entries.push((desc.name.clone(), value.borrow().copy())) - } - - TreeView::new(builder.build()) - } - - pub fn render_view(&self) -> Result<(), ShellError> { - // Set up the print configuration - let config = { - let mut config = PrintConfig::from_env(); - config.branch = Style { - foreground: Some(Color::Green), - dimmed: true, - ..Style::default() - }; - config.leaf = Style { - bold: true, - ..Style::default() - }; - config.indent = 4; - config - }; - - // Print out the tree using custom formatting - print_tree_with(&self.tree, &config)?; - - Ok(()) - } -}