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.
This commit is contained in:
Reilly Wood 2023-01-03 13:03:05 -08:00 committed by GitHub
parent b17e9f4ed0
commit d7af461173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 0 additions and 3111 deletions

View File

@ -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"] }

View File

@ -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<Bar<'a>, ShellError> {
let mut data = Vec::new();
let mut data_points = Vec::new();
for percentages in model
.percentages
.table_entries()
.cloned()
.collect::<Vec<_>>()
{
let mut percentages_collected = vec![];
for percentage in percentages.table_entries().cloned().collect::<Vec<_>>() {
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<T>(&mut self, ui: &mut tui::Terminal<T>) -> 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);
}
}
}

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_chart::ChartBar;
fn main() {
serve_plugin(&mut ChartBar::new());
}

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_chart::ChartLine;
fn main() {
serve_plugin(&mut ChartLine::new());
}

View File

@ -1,5 +0,0 @@
mod bar;
mod line;
mod nu;
pub use nu::{ChartBar, ChartLine};

View File

@ -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<String>,
x_range: [f64; 2],
y_range: [f64; 2],
datasets_names: Vec<String>,
data: Vec<Vec<(f64, f64)>>,
}
impl<'a> Line {
pub fn from_model(model: &'a Model) -> Result<Line, ShellError> {
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::<Vec<_>>()
.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::<Vec<_>>()
})
.collect::<Vec<_>>(),
})
}
pub fn draw<T>(&mut self, ui: &mut tui::Terminal<T>) -> 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::<Vec<_>>();
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(())
}
}

View File

@ -1,5 +0,0 @@
mod bar;
mod line;
pub use bar::SubCommand as ChartBar;
pub use line::SubCommand as ChartLine;

View File

@ -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<I> {
Input(I),
Tick,
}
pub enum Columns {
One(Tagged<String>),
Two(Tagged<String>, Tagged<String>),
None,
}
#[allow(clippy::type_complexity)]
pub struct SubCommand {
pub reduction: nu_data::utils::Reduction,
pub columns: Columns,
pub eval: Option<Box<dyn Fn(usize, &Value) -> Result<Value, ShellError> + Send>>,
pub format: Option<String>,
}
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<dyn Error>> {
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<Signature, ShellError> {
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<Value>) {
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<Value>) -> 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<dyn Fn(usize, &Value) -> Result<Value, ShellError> + 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),
}
})
}

View File

@ -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<I> {
Input(I),
Tick,
}
pub enum Columns {
One(Tagged<String>),
Two(Tagged<String>, Tagged<String>),
None,
}
#[allow(clippy::type_complexity)]
pub struct SubCommand {
pub reduction: nu_data::utils::Reduction,
pub columns: Columns,
pub eval: Option<Box<dyn Fn(usize, &Value) -> Result<Value, ShellError> + Send>>,
pub format: Option<String>,
}
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<dyn Error>> {
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<Signature, ShellError> {
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<Value>) {
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<Value>) -> 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<dyn Fn(usize, &Value) -> Result<Value, ShellError> + 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),
}
})
}

View File

@ -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]

View File

@ -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<u8>,
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<Vec<Value>, 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<Tag>) -> Result<Value, ShellError> {
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(&regx.pattern)))
.into_value(&tag),
);
collected.insert_value(
"$options".to_string(),
UntaggedValue::Primitive(Primitive::String(String::from(&regx.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<u8>,
}
impl BytesReader {
fn new(bytes: Vec<u8>) -> BytesReader {
BytesReader {
pos: 0,
inner: bytes,
}
}
}
impl std::io::Read for BytesReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
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<u8>, tag: impl Into<Tag>) -> Result<Value, ShellError> {
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<u8>, name_tag: Tag) -> Result<Vec<ReturnValue>, 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,
)),
}
}

View File

@ -1,4 +0,0 @@
mod from_bson;
mod nu;
pub use from_bson::FromBson;

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_from_bson::FromBson;
fn main() {
serve_plugin(&mut FromBson::new())
}

View File

@ -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<Signature, ShellError> {
Ok(Signature::build("from bson")
.usage("Convert from .bson binary into table")
.filter())
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
self.name_tag = call_info.name_tag;
Ok(vec![])
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, 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<Vec<ReturnValue>, ShellError> {
crate::from_bson::from_bson(self.state.clone(), Tag::unknown())
}
}

View File

@ -1 +0,0 @@
mod integration {}

View File

@ -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]

View File

@ -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<u8>,
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<Value, mp4::Error> {
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<u8>, tag: Tag) -> Result<Value, std::io::Error> {
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<u8>, name_tag: Tag) -> Result<Vec<ReturnValue>, 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,
)),
}
}

View File

@ -1,4 +0,0 @@
mod from_mp4;
mod nu;
pub use from_mp4::FromMp4;

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_from_mp4::FromMp4;
fn main() {
serve_plugin(&mut FromMp4::new())
}

View File

@ -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<Signature, ShellError> {
Ok(Signature::build("from mp4")
.usage("Get meta-data of mp4 file")
.filter())
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
self.name_tag = call_info.name_tag;
Ok(vec![])
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, 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<Vec<ReturnValue>, ShellError> {
crate::from_mp4::from_mp4(self.state.clone(), Tag::unknown())
}
}

View File

@ -1 +0,0 @@
mod integration {}

View File

@ -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]

View File

@ -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<u8>,
pub name_tag: Tag,
pub tables: Vec<String>,
}
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<Tag> + Clone,
tables: Vec<String>,
) -> Result<Value, rusqlite::Error> {
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<Tag> + 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<Tag> + 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<u8>,
tag: impl Into<Tag> + Clone,
tables: Vec<String>,
) -> Result<Value, std::io::Error> {
// 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<u8>,
name_tag: Tag,
tables: Vec<String>,
) -> Result<Vec<ReturnValue>, 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,
)),
}
}

View File

@ -1,4 +0,0 @@
mod from_sqlite;
mod nu;
pub use from_sqlite::FromSqlite;

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_from_sqlite::FromSqlite;
fn main() {
serve_plugin(&mut FromSqlite::new())
}

View File

@ -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<Vec<String>, 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::<Result<Vec<String>, _>>()?;
Ok(res)
}
impl Plugin for FromSqlite {
fn config(&mut self) -> Result<Signature, ShellError> {
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<Vec<ReturnValue>, 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<Vec<ReturnValue>, 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<Vec<ReturnValue>, ShellError> {
crate::from_sqlite::from_sqlite(self.state.clone(), Tag::unknown(), self.tables.clone())
}
}

View File

@ -1 +0,0 @@
mod integration {}

View File

@ -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]

View File

@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

View File

@ -1,134 +0,0 @@
use nu_errors::ShellError;
use nu_protocol::{CallInfo, CommandAction, ReturnSuccess, ReturnValue, UntaggedValue, Value};
use nu_source::{AnchorLocation, Tag};
use s3handler::{CredentialConfig, Handler as S3Handler};
pub struct Handler {
pub resource: Option<Value>,
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<String> {
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))
}
}

View File

@ -1,4 +0,0 @@
pub mod handler;
mod nu;
pub use handler::Handler;

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_s3::handler;
fn main() {
serve_plugin(&mut handler::Handler::new())
}

View File

@ -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<Signature, ShellError> {
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<Vec<ReturnValue>, 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,
))])
}
}

View File

@ -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" }

View File

@ -1,4 +0,0 @@
mod nu;
mod start;
pub use start::Start;

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_start::Start;
fn main() {
serve_plugin(&mut Start::new());
}

View File

@ -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<Signature, ShellError> {
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<Vec<ReturnValue>, ShellError> {
self.parse(call_info)?;
self.exec().map(|_| Vec::new())
}
}

View File

@ -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<Tagged<String>>,
pub application: Option<String>,
}
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<String>) -> 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<Vec<Tagged<String>>, 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::<Vec<_>>(),
);
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::<Vec<_>>(),
);
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,
))
}
}

View File

@ -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]

View File

@ -1,4 +0,0 @@
mod nu;
mod to_bson;
pub use to_bson::ToBson;

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_to_bson::ToBson;
fn main() {
serve_plugin(&mut ToBson::new())
}

View File

@ -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<Signature, ShellError> {
Ok(Signature::build("to bson")
.usage("Convert table into .bson binary")
.filter())
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> {
self.state.push(input);
Ok(vec![])
}
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
Ok(crate::to_bson::to_bson(self.state.clone(), Tag::unknown()))
}
}

View File

@ -1 +0,0 @@
mod integration {}

View File

@ -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<Value>,
}
impl ToBson {
pub fn new() -> ToBson {
ToBson { state: vec![] }
}
}
pub fn value_to_bson_value(v: &Value) -> Result<Bson, ShellError> {
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::<Result<Vec<Bson>, 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::<Result<_, _>>()?,
),
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<Bson, ShellError> {
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<String, _> = tagged_regex_value.try_into();
let opts: Result<String, _> = 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<String, _> = 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<String, _> = 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<i64, _> = 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<Vec<u8>, _> = 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<String, _> = 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<String, _> = 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<BinarySubtype, ShellError> {
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<Bson, ShellError> {
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<u8>, 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<Vec<u8>, 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<Value>, name_tag: Tag) -> Vec<ReturnValue> {
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()
}

View File

@ -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]

View File

@ -1,4 +0,0 @@
mod nu;
mod to_sqlite;
pub use to_sqlite::ToSqlite;

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_to_sqlite::ToSqlite;
fn main() {
serve_plugin(&mut ToSqlite::new())
}

View File

@ -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<Signature, ShellError> {
Ok(Signature::build("to sqlite")
.usage("Convert table into sqlite binary")
.filter())
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> {
self.state.push(input);
Ok(vec![])
}
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
crate::to_sqlite::to_sqlite(self.state.clone(), Tag::unknown())
}
}

View File

@ -1 +0,0 @@
mod integration {}

View File

@ -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<Value>,
}
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<String, std::io::Error> {
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<Value>) -> Result<String, std::io::Error> {
let values: Result<Vec<_>, _> = 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<Value>) -> Result<Value, std::io::Error> {
// 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<Value>, name_tag: Tag) -> Result<Vec<ReturnValue>, 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,
)),
}
}

View File

@ -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]

View File

@ -1,4 +0,0 @@
mod nu;
mod tree;
pub use tree::TreeViewer;

View File

@ -1,6 +0,0 @@
use nu_plugin::serve_plugin;
use nu_plugin_tree::TreeViewer;
fn main() {
serve_plugin(&mut TreeViewer);
}

View File

@ -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<Signature, ShellError> {
Ok(Signature::build("tree").usage("View the contents of the pipeline as a tree."))
}
fn sink(&mut self, _call_info: CallInfo, input: Vec<Value>) {
for i in &input {
let view = TreeView::from_value(i);
let _ = view.render_view();
}
}
}

View File

@ -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(())
}
}