forked from extern/nushell
engine-q merge
This commit is contained in:
@ -1,10 +0,0 @@
|
||||
use nu_protocol::Value;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LogLevel {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LogItem {
|
||||
level: LogLevel,
|
||||
value: Value,
|
||||
}
|
@ -1,725 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use lazy_static::lazy_static;
|
||||
use nu_engine::{evaluate_baseline_expr, BufCodecReader};
|
||||
use nu_engine::{MaybeTextCodec, StringOrBinary};
|
||||
use nu_test_support::NATIVE_PATH_ENV_VAR;
|
||||
use parking_lot::Mutex;
|
||||
use regex::Regex;
|
||||
|
||||
#[allow(unused)]
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc;
|
||||
use std::{borrow::Cow, io::BufReader};
|
||||
|
||||
use log::trace;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::Expression;
|
||||
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
|
||||
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use which::which_in;
|
||||
|
||||
pub(crate) fn run_external_command(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
trace!(target: "nu::run::external", "-> {}", command.name);
|
||||
|
||||
context.sync_path_to_env();
|
||||
if !context.host().lock().is_external_cmd(&command.name) {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Command not found",
|
||||
format!("command {} not found", &command.name),
|
||||
&command.name_tag,
|
||||
));
|
||||
}
|
||||
|
||||
run_with_stdin(command, context, input, external_redirection)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn trim_enclosing_quotes(input: &str) -> String {
|
||||
let mut chars = input.chars();
|
||||
|
||||
match (chars.next(), chars.next_back()) {
|
||||
(Some('"'), Some('"')) => chars.collect(),
|
||||
(Some('\''), Some('\'')) => chars.collect(),
|
||||
_ => input.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_with_stdin(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let path = context.shell_manager().path();
|
||||
|
||||
let mut command_args = vec![];
|
||||
for arg in command.args.iter() {
|
||||
let is_literal = matches!(arg.expr, Expression::Literal(_));
|
||||
let value = evaluate_baseline_expr(arg, context)?;
|
||||
|
||||
// Skip any arguments that don't really exist, treating them as optional
|
||||
// FIXME: we may want to preserve the gap in the future, though it's hard to say
|
||||
// what value we would put in its place.
|
||||
if value.value.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do the cleanup that we need to do on any argument going out:
|
||||
match &value.value {
|
||||
UntaggedValue::Table(table) => {
|
||||
for t in table {
|
||||
match &t.value {
|
||||
UntaggedValue::Primitive(_) => {
|
||||
command_args.push((
|
||||
t.convert_to_string().trim_end_matches('\n').to_string(),
|
||||
is_literal,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Could not convert to positional arguments",
|
||||
"could not convert to positional arguments",
|
||||
value.tag(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let trimmed_value_string = value.as_string()?.trim_end_matches('\n').to_string();
|
||||
//let trimmed_value_string = trim_quotes(&trimmed_value_string);
|
||||
command_args.push((trimmed_value_string, is_literal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let process_args = command_args
|
||||
.iter()
|
||||
.map(|(arg, _is_literal)| {
|
||||
let arg = nu_path::expand_tilde(arg).to_string_lossy().to_string();
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
if !_is_literal {
|
||||
arg
|
||||
} else {
|
||||
trim_enclosing_quotes(&arg)
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if let Some(unquoted) = remove_quotes(&arg) {
|
||||
unquoted.to_string()
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
spawn(
|
||||
&command,
|
||||
&path,
|
||||
&process_args[..],
|
||||
input,
|
||||
external_redirection,
|
||||
&context.scope,
|
||||
)
|
||||
}
|
||||
|
||||
/// Spawn a direct exe
|
||||
#[allow(unused)]
|
||||
fn spawn_exe(full_path: PathBuf, args: &[String]) -> Command {
|
||||
let mut process = Command::new(full_path);
|
||||
for arg in args {
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
/// Spawn a cmd command with `cmd /c args...`
|
||||
fn spawn_cmd_command(command: &ExternalCommand, args: &[String]) -> Command {
|
||||
let mut process = Command::new("cmd");
|
||||
process.arg("/c");
|
||||
process.arg(&command.name);
|
||||
for arg in args {
|
||||
// Clean the args before we use them:
|
||||
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
||||
// cmd.exe needs to have a caret to escape a pipe
|
||||
let arg = arg.replace("|", "^|");
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
fn has_unsafe_shell_characters(arg: &str) -> bool {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"[^\w@%+=:,./-]").expect("regex to be valid");
|
||||
}
|
||||
|
||||
RE.is_match(arg)
|
||||
}
|
||||
|
||||
fn shell_arg_escape(arg: &str) -> String {
|
||||
match arg {
|
||||
"" => String::from("''"),
|
||||
s if !has_unsafe_shell_characters(s) => String::from(s),
|
||||
_ => {
|
||||
let single_quotes_escaped = arg.split('\'').join("'\"'\"'");
|
||||
format!("'{}'", single_quotes_escaped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a sh command with `sh -c args...`
|
||||
fn spawn_sh_command(command: &ExternalCommand, args: &[String]) -> Command {
|
||||
let joined_and_escaped_arguments = args.iter().map(|arg| shell_arg_escape(arg)).join(" ");
|
||||
let cmd_with_args = vec![command.name.clone(), joined_and_escaped_arguments].join(" ");
|
||||
let mut process = Command::new("sh");
|
||||
process.arg("-c").arg(cmd_with_args);
|
||||
process
|
||||
}
|
||||
|
||||
/// a function to spawn any external command
|
||||
#[allow(unused)] // for minimal builds cwd is unused
|
||||
fn spawn_any(command: &ExternalCommand, args: &[String], cwd: &str) -> Command {
|
||||
// resolve the executable name if it is spawnable directly
|
||||
#[cfg(feature = "which")]
|
||||
// TODO add more available paths to `env::var_os("PATH")`?
|
||||
if let Result::Ok(full_path) = which_in(&command.name, env::var_os("PATH"), cwd) {
|
||||
if let Some(extension) = full_path.extension() {
|
||||
#[cfg(windows)]
|
||||
if extension.eq_ignore_ascii_case("exe") {
|
||||
// if exe spawn it directly
|
||||
return spawn_exe(full_path, args);
|
||||
} else {
|
||||
// TODO implement special care for various executable types such as .bat, .ps1, .cmd, etc
|
||||
// https://github.com/mklement0/Native/blob/e0e0b8785cad39a73053e35084d1f60d87fbac58/Native.psm1#L749
|
||||
// otherwise shell out to cmd
|
||||
return spawn_cmd_command(command, args);
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
if !["sh", "bash"]
|
||||
.iter()
|
||||
.any(|ext| extension.eq_ignore_ascii_case(ext))
|
||||
{
|
||||
// if exe spawn it directly
|
||||
return spawn_exe(full_path, args);
|
||||
} else {
|
||||
// otherwise shell out to sh
|
||||
return spawn_sh_command(command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
// in all the other cases shell out
|
||||
if cfg!(windows) {
|
||||
spawn_cmd_command(command, args)
|
||||
} else {
|
||||
// TODO what happens if that os doesn't support spawning sh?
|
||||
spawn_sh_command(command, args)
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn(
|
||||
command: &ExternalCommand,
|
||||
path: &str,
|
||||
args: &[String],
|
||||
input: InputStream,
|
||||
external_redirection: ExternalRedirection,
|
||||
scope: &Scope,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let command = command.clone();
|
||||
|
||||
let mut process = spawn_any(&command, args, path);
|
||||
process.current_dir(path);
|
||||
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
||||
|
||||
process.env_clear();
|
||||
process.envs(scope.get_env_vars());
|
||||
|
||||
// We want stdout regardless of what
|
||||
// we are doing ($it case or pipe stdin)
|
||||
match external_redirection {
|
||||
ExternalRedirection::Stdout => {
|
||||
process.stdout(Stdio::piped());
|
||||
trace!(target: "nu::run::external", "set up stdout pipe");
|
||||
}
|
||||
ExternalRedirection::Stderr => {
|
||||
process.stderr(Stdio::piped());
|
||||
trace!(target: "nu::run::external", "set up stderr pipe");
|
||||
}
|
||||
ExternalRedirection::StdoutAndStderr => {
|
||||
process.stdout(Stdio::piped());
|
||||
trace!(target: "nu::run::external", "set up stdout pipe");
|
||||
process.stderr(Stdio::piped());
|
||||
trace!(target: "nu::run::external", "set up stderr pipe");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// open since we have some contents for stdin
|
||||
if !input.is_empty() {
|
||||
process.stdin(Stdio::piped());
|
||||
trace!(target: "nu::run::external", "set up stdin pipe");
|
||||
}
|
||||
|
||||
trace!(target: "nu::run::external", "built command {:?}", process);
|
||||
|
||||
// TODO Switch to async_std::process once it's stabilized
|
||||
match process.spawn() {
|
||||
Ok(mut child) => {
|
||||
let (tx, rx) = mpsc::sync_channel(0);
|
||||
|
||||
let mut stdin = child.stdin.take();
|
||||
|
||||
let stdin_write_tx = tx.clone();
|
||||
let stdout_read_tx = tx;
|
||||
let stdin_name_tag = command.name_tag.clone();
|
||||
let stdout_name_tag = command.name_tag;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if !input.is_empty() {
|
||||
let mut stdin_write = stdin
|
||||
.take()
|
||||
.expect("Internal error: could not get stdin pipe for external command");
|
||||
|
||||
for value in input {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
if stdin_write.write(s.as_bytes()).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
UntaggedValue::Primitive(Primitive::Binary(b)) => {
|
||||
if stdin_write.write(b).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
unsupported => {
|
||||
println!("Unsupported: {:?}", unsupported);
|
||||
let _ = stdin_write_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!(
|
||||
"Received unexpected type from pipeline ({})",
|
||||
unsupported.type_name()
|
||||
),
|
||||
format!(
|
||||
"expected a string, got {} as input",
|
||||
unsupported.type_name()
|
||||
),
|
||||
stdin_name_tag.clone(),
|
||||
)),
|
||||
tag: stdin_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if external_redirection == ExternalRedirection::Stdout
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stdout = if let Some(stdout) = child.stdout.take() {
|
||||
stdout
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Can't redirect the stdout for external command",
|
||||
"can't redirect stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
// let file = futures::io::AllowStdIo::new(stdout);
|
||||
// let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
let buf_read = BufReader::new(stdout);
|
||||
let buf_codec = BufCodecReader::new(buf_read, MaybeTextCodec::default());
|
||||
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(
|
||||
s.clone(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(b) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Binary(
|
||||
b.into_iter().collect(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if external_redirection == ExternalRedirection::Stderr
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stderr = if let Some(stderr) = child.stderr.take() {
|
||||
stderr
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Can't redirect the stderr for external command",
|
||||
"can't redirect stderr",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
// let file = futures::io::AllowStdIo::new(stderr);
|
||||
// let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
let buf_reader = BufReader::new(stderr);
|
||||
let buf_codec = BufCodecReader::new(buf_reader, MaybeTextCodec::default());
|
||||
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(s),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(_) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error("<binary stderr>"),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can give an error when we see a non-zero exit code, but this is different
|
||||
// than what other shells will do.
|
||||
let external_failed = match child.wait() {
|
||||
Err(_) => true,
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
};
|
||||
|
||||
if external_failed {
|
||||
let cfg = nu_data::config::config(Tag::unknown());
|
||||
if let Ok(cfg) = cfg {
|
||||
if cfg.contains_key("nonzero_exit_errors") {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"External command failed",
|
||||
"command failed",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::nothing(),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let stream = ChannelReceiver::new(rx);
|
||||
Ok(stream.into_input_stream())
|
||||
}
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
e.to_string(),
|
||||
"failed to spawn",
|
||||
&command.name_tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
struct ChannelReceiver {
|
||||
rx: Arc<Mutex<mpsc::Receiver<Result<Value, ShellError>>>>,
|
||||
}
|
||||
|
||||
impl ChannelReceiver {
|
||||
pub fn new(rx: mpsc::Receiver<Result<Value, ShellError>>) -> Self {
|
||||
Self {
|
||||
rx: Arc::new(Mutex::new(rx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ChannelReceiver {
|
||||
type Item = Result<Value, ShellError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let rx = self.rx.lock();
|
||||
match rx.recv() {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn argument_is_quoted(argument: &str) -> bool {
|
||||
if argument.len() < 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
(argument.starts_with('"') && argument.ends_with('"'))
|
||||
|| (argument.starts_with('\'') && argument.ends_with('\''))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn add_double_quotes(argument: &str) -> String {
|
||||
format!("\"{}\"", argument)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn escape_double_quotes(argument: &str) -> Cow<'_, str> {
|
||||
// allocate new string only if required
|
||||
if argument.contains('"') {
|
||||
Cow::Owned(argument.replace('"', r#"\""#))
|
||||
} else {
|
||||
Cow::Borrowed(argument)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn remove_quotes(argument: &str) -> Option<&str> {
|
||||
if !argument_is_quoted(argument) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let size = argument.len();
|
||||
|
||||
Some(&argument[1..size - 1])
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn shell_os_paths() -> Vec<std::path::PathBuf> {
|
||||
let mut original_paths = vec![];
|
||||
|
||||
if let Some(paths) = std::env::var_os(NATIVE_PATH_ENV_VAR) {
|
||||
original_paths = std::env::split_paths(&paths).collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
original_paths
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{add_double_quotes, argument_is_quoted, escape_double_quotes, remove_quotes};
|
||||
#[cfg(feature = "which")]
|
||||
use super::{run_external_command, InputStream};
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use nu_engine::EvaluationContext;
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use nu_test_support::commands::ExternalBuilder;
|
||||
// fn read(mut stream: OutputStream) -> Option<Value> {
|
||||
// match stream.try_next() {
|
||||
// Ok(val) => {
|
||||
// if let Some(val) = val {
|
||||
// val.raw_value()
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
// Err(_) => None,
|
||||
// }
|
||||
// }
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
fn non_existent_run() {
|
||||
use nu_protocol::hir::ExternalRedirection;
|
||||
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
|
||||
|
||||
let input = InputStream::empty();
|
||||
let mut ctx = EvaluationContext::basic();
|
||||
|
||||
assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err());
|
||||
}
|
||||
|
||||
// fn failure_run() -> Result<(), ShellError> {
|
||||
// let cmd = ExternalBuilder::for_name("fail").build();
|
||||
|
||||
// let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context.");
|
||||
// let stream = run_external_command(cmd, &mut ctx, None, false)
|
||||
// ?
|
||||
// .expect("There was a problem running the external command.");
|
||||
|
||||
// match read(stream.into()) {
|
||||
// Some(Value {
|
||||
// value: UntaggedValue::Error(_),
|
||||
// ..
|
||||
// }) => {}
|
||||
// None | _ => panic!("Command didn't fail."),
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn identifies_command_failed() -> Result<(), ShellError> {
|
||||
// block_on(failure_run())
|
||||
// }
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
#[test]
|
||||
fn identifies_command_not_found() {
|
||||
non_existent_run()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checks_escape_double_quotes() {
|
||||
assert_eq!(escape_double_quotes("andrés"), "andrés");
|
||||
assert_eq!(escape_double_quotes(r#"an"drés"#), r#"an\"drés"#);
|
||||
assert_eq!(escape_double_quotes(r#""an"drés""#), r#"\"an\"drés\""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checks_quotes_from_argument_to_be_passed_in() {
|
||||
assert!(!argument_is_quoted(""));
|
||||
|
||||
assert!(!argument_is_quoted("'"));
|
||||
assert!(!argument_is_quoted("'a"));
|
||||
assert!(!argument_is_quoted("a"));
|
||||
assert!(!argument_is_quoted("a'"));
|
||||
assert!(argument_is_quoted("''"));
|
||||
|
||||
assert!(!argument_is_quoted(r#"""#));
|
||||
assert!(!argument_is_quoted(r#""a"#));
|
||||
assert!(!argument_is_quoted(r#"a"#));
|
||||
assert!(!argument_is_quoted(r#"a""#));
|
||||
assert!(argument_is_quoted(r#""""#));
|
||||
|
||||
assert!(!argument_is_quoted("'andrés"));
|
||||
assert!(!argument_is_quoted("andrés'"));
|
||||
assert!(!argument_is_quoted(r#""andrés"#));
|
||||
assert!(!argument_is_quoted(r#"andrés""#));
|
||||
assert!(argument_is_quoted("'andrés'"));
|
||||
assert!(argument_is_quoted(r#""andrés""#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_double_quotes_to_argument_to_be_passed_in() {
|
||||
assert_eq!(add_double_quotes("andrés"), "\"andrés\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strips_quotes_from_argument_to_be_passed_in() {
|
||||
assert_eq!(remove_quotes(""), None);
|
||||
|
||||
assert_eq!(remove_quotes("'"), None);
|
||||
assert_eq!(remove_quotes("'a"), None);
|
||||
assert_eq!(remove_quotes("a"), None);
|
||||
assert_eq!(remove_quotes("a'"), None);
|
||||
assert_eq!(remove_quotes("''"), Some(""));
|
||||
|
||||
assert_eq!(remove_quotes(r#"""#), None);
|
||||
assert_eq!(remove_quotes(r#""a"#), None);
|
||||
assert_eq!(remove_quotes(r#"a"#), None);
|
||||
assert_eq!(remove_quotes(r#"a""#), None);
|
||||
assert_eq!(remove_quotes(r#""""#), Some(""));
|
||||
|
||||
assert_eq!(remove_quotes("'andrés"), None);
|
||||
assert_eq!(remove_quotes("andrés'"), None);
|
||||
assert_eq!(remove_quotes(r#""andrés"#), None);
|
||||
assert_eq!(remove_quotes(r#"andrés""#), None);
|
||||
assert_eq!(remove_quotes("'andrés'"), Some("andrés"));
|
||||
assert_eq!(remove_quotes(r#""andrés""#), Some("andrés"));
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
pub(crate) mod external;
|
@ -1,46 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Chart;
|
||||
|
||||
impl WholeStreamCommand for Chart {
|
||||
fn name(&self) -> &str {
|
||||
"chart"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("chart")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Displays charts."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
if args.scope().get_command("chart bar").is_none() {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"nu_plugin_chart not installed.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(ActionStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(get_full_help(&Chart, args.scope())).into_value(Tag::unknown()),
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Chart;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Chart {})
|
||||
}
|
||||
}
|
@ -1,220 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
ColumnPath, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Histogram;
|
||||
|
||||
impl WholeStreamCommand for Histogram {
|
||||
fn name(&self) -> &str {
|
||||
"histogram"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("histogram")
|
||||
.named(
|
||||
"use",
|
||||
SyntaxShape::ColumnPath,
|
||||
"Use data at the column path given as valuator",
|
||||
None,
|
||||
)
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column name to give the histogram's frequency column",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Creates a new table with a histogram based on the column name passed in."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
histogram(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Get a histogram for the types of files",
|
||||
example: "ls | histogram type",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Get a histogram for the types of files, with frequency column named percentage",
|
||||
example: "ls | histogram type percentage",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Get a histogram for a list of numbers",
|
||||
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn histogram(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
|
||||
let mut columns = args.rest::<ColumnPath>(0)?;
|
||||
let evaluate_with = args.get_flag::<ColumnPath>("use")?.map(evaluator);
|
||||
let values: Vec<Value> = args.input.collect();
|
||||
|
||||
let column_grouper = if !columns.is_empty() {
|
||||
columns
|
||||
.remove(0)
|
||||
.split_last()
|
||||
.map(|(key, _)| key.as_string().tagged(&name))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let frequency_column_name = if columns.is_empty() {
|
||||
"frequency".to_string()
|
||||
} else if let Some((key, _)) = columns[0].split_last() {
|
||||
key.as_string()
|
||||
} else {
|
||||
"frequency".to_string()
|
||||
};
|
||||
|
||||
let column = if let Some(ref column) = column_grouper {
|
||||
column.clone()
|
||||
} else {
|
||||
"value".to_string().tagged(&name)
|
||||
};
|
||||
|
||||
let results = nu_data::utils::report(
|
||||
&UntaggedValue::table(&values).into_value(&name),
|
||||
nu_data::utils::Operation {
|
||||
grouper: Some(Box::new(move |_, _| Ok(String::from("frequencies")))),
|
||||
splitter: Some(splitter(column_grouper)),
|
||||
format: &None,
|
||||
eval: &evaluate_with,
|
||||
reduction: &nu_data::utils::Reduction::Count,
|
||||
},
|
||||
&name,
|
||||
)?;
|
||||
|
||||
let labels = results.labels.y.clone();
|
||||
let mut idx = 0;
|
||||
|
||||
Ok(results
|
||||
.data
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.zip(
|
||||
results
|
||||
.percentages
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter(),
|
||||
)
|
||||
.map(move |(counts, percentages)| {
|
||||
let percentage = percentages
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.last()
|
||||
.unwrap_or_else(|| {
|
||||
UntaggedValue::decimal_from_float(0.0, name.span).into_value(&name)
|
||||
});
|
||||
let value = counts
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.last()
|
||||
.unwrap_or_else(|| UntaggedValue::int(0).into_value(&name));
|
||||
|
||||
let mut fact = TaggedDictBuilder::new(&name);
|
||||
let column_value = labels
|
||||
.get(idx)
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
"Unable to load group labels",
|
||||
"unable to load group labels",
|
||||
&name,
|
||||
)
|
||||
})?
|
||||
.clone();
|
||||
|
||||
fact.insert_value(&column.item, column_value);
|
||||
fact.insert_untagged("count", value);
|
||||
|
||||
let fmt_percentage = format!(
|
||||
"{}%",
|
||||
// Some(2) < the number of digits
|
||||
// true < group the digits
|
||||
crate::commands::conversions::into::string::action(
|
||||
&percentage,
|
||||
&name,
|
||||
Some(2),
|
||||
true
|
||||
)?
|
||||
.as_string()?
|
||||
);
|
||||
fact.insert_untagged("percentage", UntaggedValue::string(fmt_percentage));
|
||||
|
||||
let string = "*".repeat(percentage.as_u64().map_err(|_| {
|
||||
ShellError::labeled_error("expected a number", "expected a number", &name)
|
||||
})? as usize);
|
||||
|
||||
fact.insert_untagged(&frequency_column_name, UntaggedValue::string(string));
|
||||
|
||||
idx += 1;
|
||||
|
||||
ReturnSuccess::value(fact.into_value())
|
||||
})
|
||||
.into_action_stream())
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn splitter(
|
||||
by: Option<Tagged<String>>,
|
||||
) -> Box<dyn Fn(usize, &Value) -> Result<String, ShellError> + Send> {
|
||||
match by {
|
||||
Some(column) => Box::new(move |_, row: &Value| {
|
||||
let key = &column;
|
||||
|
||||
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(),
|
||||
)),
|
||||
}
|
||||
}),
|
||||
None => Box::new(move |_, row: &Value| nu_value_ext::as_string(row)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Histogram;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Histogram {})
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
mod chart;
|
||||
mod histogram;
|
||||
|
||||
pub use chart::Chart;
|
||||
pub use histogram::Histogram;
|
@ -1,53 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config clear"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config clear")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"clear the config"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
clear(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Clear the config (be careful!)",
|
||||
example: "config clear",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = &args.context;
|
||||
|
||||
let result = if let Some(global_cfg) = &mut args.configs().lock().global_config {
|
||||
global_cfg.vars.clear();
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
let value = UntaggedValue::Row(global_cfg.vars.clone().into()).into_value(name);
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::CommandArgs;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"config"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Configuration management."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
|
||||
if let Some(global_cfg) = &args.configs().lock().global_config {
|
||||
let result = global_cfg.vars.clone();
|
||||
let value = UntaggedValue::Row(result.into()).into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config get"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config get").required(
|
||||
"get",
|
||||
SyntaxShape::ColumnPath,
|
||||
"value to get from the config",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Gets a value from the config"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
get(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Get the current startup commands",
|
||||
example: "config get startup",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = &args.context;
|
||||
|
||||
let column_path = args.req(0)?;
|
||||
|
||||
let result = if let Some(global_cfg) = &ctx.configs().lock().global_config {
|
||||
let result = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
|
||||
let value = crate::commands::filters::get::get_column_path(&column_path, &result)?;
|
||||
Ok(match value {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => OutputStream::from_stream(list.into_iter()),
|
||||
x => OutputStream::one(x),
|
||||
})
|
||||
} else {
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
pub mod clear;
|
||||
pub mod command;
|
||||
pub mod get;
|
||||
pub mod path;
|
||||
pub mod remove;
|
||||
pub mod set;
|
||||
pub mod set_into;
|
||||
|
||||
pub use clear::SubCommand as ConfigClear;
|
||||
pub use command::Command as Config;
|
||||
pub use get::SubCommand as ConfigGet;
|
||||
pub use path::SubCommand as ConfigPath;
|
||||
pub use remove::SubCommand as ConfigRemove;
|
||||
pub use set::SubCommand as ConfigSet;
|
||||
pub use set_into::SubCommand as ConfigSetInto;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
|
||||
pub fn err_no_global_cfg_present() -> ShellError {
|
||||
ShellError::untagged_runtime_error("No global config found!")
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config path"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config path")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"return the path to the config file"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
path(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Get the path to the current config file",
|
||||
example: "config path",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
|
||||
if let Some(global_cfg) = &mut args.configs().lock().global_config {
|
||||
let value = UntaggedValue::Primitive(Primitive::FilePath(global_cfg.file_path.clone()))
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config remove"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config remove").required(
|
||||
"remove",
|
||||
SyntaxShape::Any,
|
||||
"remove a value from the config",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Removes a value from the config"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
remove(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Remove the startup commands",
|
||||
example: "config remove startup",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = &args.context;
|
||||
let remove: Tagged<String> = args.req(0)?;
|
||||
|
||||
let key = remove.to_string();
|
||||
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs().lock().global_config {
|
||||
if global_cfg.vars.contains_key(&key) {
|
||||
global_cfg.vars.swap_remove(&key);
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
let value: Value = UntaggedValue::row(global_cfg.vars.clone()).into_value(remove.tag);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Key does not exist in config",
|
||||
"key",
|
||||
remove.tag(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config set"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config set")
|
||||
.required("key", SyntaxShape::ColumnPath, "variable name to set")
|
||||
.required("value", SyntaxShape::Any, "value to use")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Sets a value in the config"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Set auto pivoting",
|
||||
example: "config set pivot_mode always",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set line editor options",
|
||||
example: "config set line_editor [[edit_mode, completion_type]; [emacs circular]]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set coloring options",
|
||||
example: "config set color_config [[header_align header_color]; [left white_bold]]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set nested options",
|
||||
example: "config set color_config.header_color white",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = &args.context;
|
||||
|
||||
let column_path = args.req(0)?;
|
||||
let mut value: Value = args.req(1)?;
|
||||
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs().lock().global_config {
|
||||
let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
|
||||
|
||||
if let UntaggedValue::Table(rows) = &value.value {
|
||||
if rows.len() == 1 && rows[0].is_row() {
|
||||
value = rows[0].clone();
|
||||
}
|
||||
}
|
||||
|
||||
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
|
||||
Ok(Value {
|
||||
value: UntaggedValue::Row(changes),
|
||||
..
|
||||
}) => {
|
||||
global_cfg.vars = changes.entries;
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
let value = UntaggedValue::row(global_cfg.vars.clone()).into_value(name);
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
Ok(_) => Ok(OutputStream::empty()),
|
||||
Err(reason) => Err(reason),
|
||||
}
|
||||
} else {
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config set_into"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config set_into").required(
|
||||
"set_into",
|
||||
SyntaxShape::String,
|
||||
"sets a variable from values in the pipeline",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Sets a value in the config"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set_into(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Store the contents of the pipeline as a path",
|
||||
example: "echo ['/usr/bin' '/bin'] | config set_into path",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = &args.context;
|
||||
|
||||
let set_into: Tagged<String> = args.req(0)?;
|
||||
|
||||
let rows: Vec<Value> = args.input.collect();
|
||||
let key = set_into.to_string();
|
||||
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs().lock().global_config {
|
||||
if rows.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"No values given for set_into",
|
||||
"needs value(s) from pipeline",
|
||||
set_into.tag(),
|
||||
));
|
||||
} else if rows.len() == 1 {
|
||||
// A single value
|
||||
let value = &rows[0];
|
||||
|
||||
global_cfg.vars.insert(key, value.clone());
|
||||
} else {
|
||||
// Take in the pipeline as a table
|
||||
let value = UntaggedValue::Table(rows).into_value(name.clone());
|
||||
|
||||
global_cfg.vars.insert(key, value);
|
||||
}
|
||||
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
let value = UntaggedValue::row(global_cfg.vars.clone()).into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use num_bigint::{BigInt, ToBigInt};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into binary"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into binary").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to binary (for table input)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to a binary primitive"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_binary(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "convert string to a nushell binary primitive",
|
||||
example:
|
||||
"echo 'This is a string that is exactly 52 characters long.' | into binary",
|
||||
result: Some(vec![UntaggedValue::binary(
|
||||
"This is a string that is exactly 52 characters long."
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.into()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert a number to a nushell binary primitive",
|
||||
example: "echo 1 | into binary",
|
||||
result: Some(vec![UntaggedValue::binary(
|
||||
i64::from(1).to_le_bytes().to_vec(),
|
||||
)
|
||||
.into()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert a boolean to a nushell binary primitive",
|
||||
example: "echo $true | into binary",
|
||||
result: Some(vec![UntaggedValue::binary(
|
||||
i64::from(1).to_le_bytes().to_vec(),
|
||||
)
|
||||
.into()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert a filesize to a nushell binary primitive",
|
||||
example: "ls | where name == LICENSE | get size | into binary",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "convert a filepath to a nushell binary primitive",
|
||||
example: "ls | where name == LICENSE | get name | path expand | into binary",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "convert a decimal to a nushell binary primitive",
|
||||
example: "echo 1.234 | into binary",
|
||||
result: Some(vec![
|
||||
UntaggedValue::binary(BigInt::from(1).to_bytes_le().1).into()
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_binary(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
fn int_to_endian(n: i64) -> Vec<u8> {
|
||||
if cfg!(target_endian = "little") {
|
||||
n.to_le_bytes().to_vec()
|
||||
} else {
|
||||
n.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn bigint_to_endian(n: &BigInt) -> Vec<u8> {
|
||||
if cfg!(target_endian = "little") {
|
||||
n.to_bytes_le().1
|
||||
} else {
|
||||
n.to_bytes_be().1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::binary(match prim {
|
||||
Primitive::Binary(b) => b.to_vec(),
|
||||
Primitive::Int(n_ref) => int_to_endian(*n_ref),
|
||||
Primitive::BigInt(n_ref) => bigint_to_endian(n_ref),
|
||||
Primitive::Decimal(dec) => match dec.to_bigint() {
|
||||
Some(n) => bigint_to_endian(&n),
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to int",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Filesize(a_filesize) => match a_filesize.to_bigint() {
|
||||
Some(n) => bigint_to_endian(&n),
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert filesize to bigint",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::String(a_string) => a_string.as_bytes().to_vec(),
|
||||
Primitive::Boolean(a_bool) => match a_bool {
|
||||
false => int_to_endian(0),
|
||||
true => int_to_endian(1),
|
||||
},
|
||||
Primitive::Date(a_date) => a_date.format("%c").to_string().as_bytes().to_vec(),
|
||||
Primitive::FilePath(a_filepath) => a_filepath
|
||||
.as_path()
|
||||
.display()
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into binary' for non-numeric primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into binary COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into binary' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into column-path"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into column-path").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"values to convert to column path",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to column path"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_filepath(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to column path in table",
|
||||
example: "echo [[name]; ['/dev/null'] ['C:\\Program Files'] ['../../Cargo.toml']] | into column-path name",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("/dev/null", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("C:\\Program Files", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("../../Cargo.toml", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to column path",
|
||||
example: "echo 'Cargo.toml' | into column-path",
|
||||
result: Some(vec![UntaggedValue::column_path("Cargo.toml", Span::unknown()).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filepath(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::column_path(
|
||||
match prim {
|
||||
Primitive::String(a_string) => a_string,
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into column-path' for non-string primitives",
|
||||
))
|
||||
}
|
||||
},
|
||||
Span::unknown(),
|
||||
)
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into column-path COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into column-path' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"into"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Apply into function."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into path"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into path").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to filepath (for table input)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to filepath"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_filepath(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to filepath in table",
|
||||
example: "echo [[name]; ['/dev/null'] ['C:\\Program Files'] ['../../Cargo.toml']] | into path name",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::filepath("/dev/null").into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::filepath("C:\\Program Files").into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::filepath("../../Cargo.toml").into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to filepath",
|
||||
example: "echo 'Cargo.toml' | into path",
|
||||
result: Some(vec![UntaggedValue::filepath("Cargo.toml").into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filepath(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::filepath(match prim {
|
||||
Primitive::String(a_string) => match filepath_from_string(a_string, &tag) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
Primitive::FilePath(a_filepath) => a_filepath.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into path' for non-string primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into path COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into path' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn filepath_from_string(a_string: &str, _tag: &Tag) -> Result<PathBuf, ShellError> {
|
||||
Ok(PathBuf::from(a_string))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use num_bigint::ToBigInt;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into filesize"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into filesize").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to filesize (for table input)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to filesize"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_filesize(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to filesize in table",
|
||||
example: "echo [[bytes]; ['5'] [3.2] [4] [2kb]] | into filesize bytes",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(5).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(3).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(4).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(2000).into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to filesize",
|
||||
example: "echo '2' | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(2).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert decimal to filesize",
|
||||
example: "echo 8.3 | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(8).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert int to filesize",
|
||||
example: "echo 5 | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert file size to filesize",
|
||||
example: "echo 4KB | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(4000).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filesize(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::filesize(match prim {
|
||||
Primitive::String(a_string) => match int_from_string(a_string.trim(), &tag) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
Primitive::Decimal(dec) => match dec.to_bigint() {
|
||||
Some(n) => match n.to_u64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to filesize",
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to filesize",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Int(n_ref) => (*n_ref).try_into().map_err(|_| {
|
||||
ShellError::unimplemented("cannot convert negative integer to filesize")
|
||||
})?,
|
||||
Primitive::Filesize(a_filesize) => *a_filesize,
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into filesize' for non-numeric primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into filesize COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into filesize' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn int_from_string(a_string: &str, tag: &Tag) -> Result<u64, ShellError> {
|
||||
match a_string.parse::<u64>() {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => match a_string.parse::<f64>() {
|
||||
Ok(f) => match f.to_u64() {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to filesize",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to filesize",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use num_bigint::ToBigInt;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into int"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into int").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to int (for table input)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to integer"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_int(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to integer in table",
|
||||
example: "echo [[num]; ['-5'] [4] [1.5]] | into int num",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"num".to_string() => UntaggedValue::int(-5).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"num".to_string() => UntaggedValue::int(4).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"num".to_string() => UntaggedValue::int(1).into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to integer",
|
||||
example: "echo '2' | into int",
|
||||
result: Some(vec![UntaggedValue::int(2).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert decimal to integer",
|
||||
example: "echo 5.9 | into int",
|
||||
result: Some(vec![UntaggedValue::int(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert decimal string to integer",
|
||||
example: "echo '5.9' | into int",
|
||||
result: Some(vec![UntaggedValue::int(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert file size to integer",
|
||||
example: "echo 4KB | into int",
|
||||
result: Some(vec![UntaggedValue::int(4000).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert bool to integer",
|
||||
example: "echo $false $true | into int",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_int(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::int(match prim {
|
||||
Primitive::String(a_string) => match int_from_string(a_string, &tag) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
Primitive::Decimal(dec) => match dec.to_bigint() {
|
||||
Some(n) => match n.to_i64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to int",
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to int",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Int(n_ref) => *n_ref,
|
||||
Primitive::Boolean(a_bool) => match a_bool {
|
||||
false => 0,
|
||||
true => 1,
|
||||
},
|
||||
Primitive::Filesize(a_filesize) => match a_filesize.to_bigint() {
|
||||
Some(n) => match n.to_i64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert filesize to bigint",
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert filesize to bigint",
|
||||
));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into int' for non-numeric primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into int COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented("'into int' for unsupported type")),
|
||||
}
|
||||
}
|
||||
|
||||
fn int_from_string(a_string: &str, tag: &Tag) -> Result<i64, ShellError> {
|
||||
match a_string.parse::<i64>() {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => match a_string.parse::<f64>() {
|
||||
Ok(f) => match f.to_i64() {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to int",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to int",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,279 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use num_bigint::{BigInt, BigUint, ToBigInt};
|
||||
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
|
||||
use nu_data::base::shape::InlineShape;
|
||||
use num_format::Locale;
|
||||
use num_traits::{Pow, Signed};
|
||||
use std::iter;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into string"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into string")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to string (for table input)",
|
||||
)
|
||||
.named(
|
||||
"decimals",
|
||||
SyntaxShape::Int,
|
||||
"decimal digits to which to round",
|
||||
Some('d'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to string"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_string(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "convert decimal to string and round to nearest integer",
|
||||
example: "echo 1.7 | into string -d 0",
|
||||
result: Some(vec![UntaggedValue::string("2").into_untagged_value()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert decimal to string",
|
||||
example: "echo 4.3 | into string",
|
||||
result: Some(vec![UntaggedValue::string("4.3").into_untagged_value()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert string to string",
|
||||
example: "echo '1234' | into string",
|
||||
result: Some(vec![UntaggedValue::string("1234").into_untagged_value()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert boolean to string",
|
||||
example: "echo $true | into string",
|
||||
result: Some(vec![UntaggedValue::string("true").into_untagged_value()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert date to string",
|
||||
example: "date now | into string",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "convert filepath to string",
|
||||
example: "ls Cargo.toml | get name | into string",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "convert filesize to string",
|
||||
example: "ls Cargo.toml | get size | into string",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_string(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let decimals: Option<Tagged<u64>> = args.get_flag("decimals")?;
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
let digits = decimals.as_ref().map(|tagged| tagged.item);
|
||||
let group_digits = false;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag(), digits, group_digits)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag(), digits, group_digits)),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(
|
||||
input: &Value,
|
||||
tag: impl Into<Tag>,
|
||||
digits: Option<u64>,
|
||||
group_digits: bool,
|
||||
) -> Result<Value, ShellError> {
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::string(match prim {
|
||||
Primitive::Int(int) => {
|
||||
if group_digits {
|
||||
format_int(*int) // int.to_formatted_string(*locale)
|
||||
} else {
|
||||
int.to_string()
|
||||
}
|
||||
}
|
||||
Primitive::BigInt(int) => {
|
||||
if group_digits {
|
||||
format_bigint(int) // int.to_formatted_string(*locale)
|
||||
} else {
|
||||
int.to_string()
|
||||
}
|
||||
}
|
||||
Primitive::Decimal(dec) => format_decimal(dec.clone(), digits, group_digits),
|
||||
Primitive::String(a_string) => a_string.to_string(),
|
||||
Primitive::Boolean(a_bool) => a_bool.to_string(),
|
||||
Primitive::Date(a_date) => a_date.format("%c").to_string(),
|
||||
Primitive::FilePath(a_filepath) => a_filepath.as_path().display().to_string(),
|
||||
Primitive::Filesize(a_filesize) => {
|
||||
let byte_string = InlineShape::format_bytes(*a_filesize, None);
|
||||
byte_string.1
|
||||
}
|
||||
Primitive::Nothing => "nothing".to_string(),
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(&format!(
|
||||
"into string for primitive: {:?}",
|
||||
prim
|
||||
)))
|
||||
}
|
||||
})
|
||||
.into_value(tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column to use 'into string'",
|
||||
"found table",
|
||||
input.tag.clone(),
|
||||
)),
|
||||
UntaggedValue::Table(_) => Err(ShellError::unimplemented("into string for table")),
|
||||
_ => Err(ShellError::unimplemented("into string for non-primitive")),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_int(int: i64) -> String {
|
||||
int.to_string()
|
||||
|
||||
// TODO once platform-specific dependencies are stable (see Cargo.toml)
|
||||
// #[cfg(windows)]
|
||||
// {
|
||||
// int.to_formatted_string(&Locale::en)
|
||||
// }
|
||||
// #[cfg(not(windows))]
|
||||
// {
|
||||
// match SystemLocale::default() {
|
||||
// Ok(locale) => int.to_formatted_string(&locale),
|
||||
// Err(_) => int.to_formatted_string(&Locale::en),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn format_bigint(int: &BigInt) -> String {
|
||||
int.to_string()
|
||||
|
||||
// TODO once platform-specific dependencies are stable (see Cargo.toml)
|
||||
// #[cfg(windows)]
|
||||
// {
|
||||
// int.to_formatted_string(&Locale::en)
|
||||
// }
|
||||
// #[cfg(not(windows))]
|
||||
// {
|
||||
// match SystemLocale::default() {
|
||||
// Ok(locale) => int.to_formatted_string(&locale),
|
||||
// Err(_) => int.to_formatted_string(&Locale::en),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn format_decimal(mut decimal: BigDecimal, digits: Option<u64>, group_digits: bool) -> String {
|
||||
if let Some(n) = digits {
|
||||
decimal = round_decimal(&decimal, n)
|
||||
}
|
||||
|
||||
if decimal.is_integer() && (digits.is_none() || digits == Some(0)) {
|
||||
let int = decimal
|
||||
.to_bigint()
|
||||
.expect("integer BigDecimal should convert to BigInt");
|
||||
return if group_digits {
|
||||
int.to_string()
|
||||
} else {
|
||||
format_bigint(&int)
|
||||
};
|
||||
}
|
||||
|
||||
let (int, exp) = decimal.as_bigint_and_exponent();
|
||||
let factor = BigInt::from(10).pow(BigUint::from(exp as u64)); // exp > 0 for non-int decimal
|
||||
let int_part = &int / &factor;
|
||||
let dec_part = (&int % &factor)
|
||||
.abs()
|
||||
.to_biguint()
|
||||
.expect("BigInt::abs should always produce positive signed BigInt and thus BigUInt")
|
||||
.to_str_radix(10);
|
||||
|
||||
let dec_str = if let Some(n) = digits {
|
||||
dec_part
|
||||
.chars()
|
||||
.chain(iter::repeat('0'))
|
||||
.take(n as usize)
|
||||
.collect()
|
||||
} else {
|
||||
String::from(dec_part.trim_end_matches('0'))
|
||||
};
|
||||
|
||||
let format_default_loc = |int_part: BigInt| {
|
||||
let loc = Locale::en;
|
||||
//TODO: when num_format is available for recent bigint, replace this with the locale-based format
|
||||
let (int_str, sep) = (int_part.to_string(), String::from(loc.decimal()));
|
||||
|
||||
format!("{}{}{}", int_str, sep, dec_str)
|
||||
};
|
||||
|
||||
format_default_loc(int_part)
|
||||
|
||||
// TODO once platform-specific dependencies are stable (see Cargo.toml)
|
||||
// #[cfg(windows)]
|
||||
// {
|
||||
// format_default_loc(int_part)
|
||||
// }
|
||||
// #[cfg(not(windows))]
|
||||
// {
|
||||
// match SystemLocale::default() {
|
||||
// Ok(sys_loc) => {
|
||||
// let int_str = int_part.to_formatted_string(&sys_loc);
|
||||
// let sep = String::from(sys_loc.decimal());
|
||||
// format!("{}{}{}", int_str, sep, dec_str)
|
||||
// }
|
||||
// Err(_) => format_default_loc(int_part),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn round_decimal(decimal: &BigDecimal, mut digits: u64) -> BigDecimal {
|
||||
let mut mag = decimal.clone();
|
||||
while mag >= BigDecimal::from(1) {
|
||||
mag = mag / 10;
|
||||
digits += 1;
|
||||
}
|
||||
|
||||
decimal.with_prec(digits)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
|
||||
pub struct Alias;
|
||||
|
||||
impl WholeStreamCommand for Alias {
|
||||
fn name(&self) -> &str {
|
||||
"alias"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("alias")
|
||||
.required("name", SyntaxShape::String, "the name of the alias")
|
||||
.required("equals", SyntaxShape::String, "the equals sign")
|
||||
.rest("rest", SyntaxShape::Any, "the expansion for the alias")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Alias a command to an expansion."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
alias(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Alias ll to ls -l",
|
||||
example: "alias ll = ls -l",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alias(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
// TODO: is there a better way of checking whether no arguments were passed?
|
||||
if args.nth(0).is_none() {
|
||||
let aliases = UntaggedValue::string(
|
||||
&args
|
||||
.scope()
|
||||
.get_aliases()
|
||||
.iter()
|
||||
.map(|val| format!("{} = '{}'", val.0, val.1.iter().map(|x| &x.item).join(" ")))
|
||||
.join("\n"),
|
||||
);
|
||||
return Ok(OutputStream::one(aliases));
|
||||
}
|
||||
Ok(OutputStream::empty())
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct Debug;
|
||||
|
||||
impl WholeStreamCommand for Debug {
|
||||
fn name(&self) -> &str {
|
||||
"debug"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("debug").switch("raw", "Prints the raw value representation.", Some('r'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Print the Rust debug representation of the values."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
debug_value(args)
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_value(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let raw = args.has_flag("raw");
|
||||
let input = args.input;
|
||||
|
||||
Ok(input
|
||||
.map(move |v| {
|
||||
if raw {
|
||||
ReturnSuccess::value(
|
||||
UntaggedValue::string(format!("{:#?}", v)).into_untagged_value(),
|
||||
)
|
||||
} else {
|
||||
ReturnSuccess::debug_value(v)
|
||||
}
|
||||
})
|
||||
.into_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Debug;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Debug {})
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, Signature, SyntaxShape, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Def;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DefArgs {
|
||||
pub name: Tagged<String>,
|
||||
pub args: Tagged<Vec<Value>>,
|
||||
pub block: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Def {
|
||||
fn name(&self) -> &str {
|
||||
"def"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("def")
|
||||
.required("name", SyntaxShape::String, "the name of the command")
|
||||
.required(
|
||||
"params",
|
||||
SyntaxShape::Table,
|
||||
"the parameters of the command",
|
||||
)
|
||||
.required("block", SyntaxShape::Block, "the body of the command")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create a command and set it to a definition."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, _args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
// Currently, we don't do anything here because we should have already
|
||||
// installed the definition as we entered the scope
|
||||
// We just create a command so that we can get proper coloring
|
||||
Ok(ActionStream::empty())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![]
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct Describe;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DescribeArgs {}
|
||||
|
||||
impl WholeStreamCommand for Describe {
|
||||
fn name(&self) -> &str {
|
||||
"describe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("describe")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Describes the objects in the stream."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
describe(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn describe(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(args
|
||||
.input
|
||||
.map(|row| {
|
||||
let name = value::plain_type(&row, 100);
|
||||
ReturnSuccess::value(
|
||||
UntaggedValue::string(name).into_value(Tag::unknown_anchor(row.tag.span)),
|
||||
)
|
||||
})
|
||||
.into_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Describe;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Describe {})
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, hir::ExternalRedirection, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct Do;
|
||||
|
||||
struct DoArgs {
|
||||
block: CapturedBlock,
|
||||
ignore_errors: bool,
|
||||
rest: Vec<Value>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Do {
|
||||
fn name(&self) -> &str {
|
||||
"do"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("do")
|
||||
.required("block", SyntaxShape::Block, "the block to run ")
|
||||
.switch(
|
||||
"ignore-errors",
|
||||
"ignore errors as the block runs",
|
||||
Some('i'),
|
||||
)
|
||||
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Runs a block, optionally ignoring errors."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
do_(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Run the block",
|
||||
example: r#"do { echo hello }"#,
|
||||
result: Some(vec![Value::from("hello")]),
|
||||
},
|
||||
Example {
|
||||
description: "Run the block and ignore errors",
|
||||
example: r#"do -i { thisisnotarealcommand }"#,
|
||||
result: Some(vec![]),
|
||||
},
|
||||
Example {
|
||||
description: "Run the block with a parameter",
|
||||
example: r#"do { |x| $x + 100 } 55"#,
|
||||
result: Some(vec![UntaggedValue::int(155).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn do_(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let external_redirection = args.call_info.args.external_redirection;
|
||||
|
||||
let context = args.context().clone();
|
||||
let do_args = DoArgs {
|
||||
block: args.req(0)?,
|
||||
ignore_errors: args.has_flag("ignore-errors"),
|
||||
rest: args.rest(1)?,
|
||||
};
|
||||
|
||||
let block_redirection = match external_redirection {
|
||||
ExternalRedirection::None => {
|
||||
if do_args.ignore_errors {
|
||||
ExternalRedirection::Stderr
|
||||
} else {
|
||||
ExternalRedirection::None
|
||||
}
|
||||
}
|
||||
ExternalRedirection::Stdout => {
|
||||
if do_args.ignore_errors {
|
||||
ExternalRedirection::StdoutAndStderr
|
||||
} else {
|
||||
ExternalRedirection::Stdout
|
||||
}
|
||||
}
|
||||
x => x,
|
||||
};
|
||||
|
||||
context.scope.enter_scope();
|
||||
|
||||
context.scope.add_vars(&do_args.block.captured.entries);
|
||||
|
||||
for (param, value) in do_args
|
||||
.block
|
||||
.block
|
||||
.params
|
||||
.positional
|
||||
.iter()
|
||||
.zip(do_args.rest)
|
||||
{
|
||||
context.scope.add_var(param.0.name(), value.clone());
|
||||
}
|
||||
|
||||
let result = run_block(
|
||||
&do_args.block.block,
|
||||
&context,
|
||||
args.input,
|
||||
block_redirection,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
|
||||
if do_args.ignore_errors {
|
||||
// To properly ignore errors we need to redirect stderr, consume it, and remove
|
||||
// any errors we see in the process.
|
||||
|
||||
match result {
|
||||
Ok(mut stream) => {
|
||||
let output = stream.drain_vec();
|
||||
context.clear_errors();
|
||||
Ok(output.into_iter().into_output_stream())
|
||||
}
|
||||
Err(_) => Ok(OutputStream::empty()),
|
||||
}
|
||||
} else {
|
||||
result.map(|x| x.into_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Do;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Do {})
|
||||
}
|
||||
}
|
@ -1,207 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::Operator;
|
||||
use nu_protocol::{Primitive, Range, RangeInclusion, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Echo;
|
||||
|
||||
impl WholeStreamCommand for Echo {
|
||||
fn name(&self) -> &str {
|
||||
"echo"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("echo").rest("rest", SyntaxShape::Any, "the values to echo")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Echo the arguments back to the user."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
echo(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Put a hello message in the pipeline",
|
||||
example: "echo 'hello'",
|
||||
result: Some(vec![Value::from("hello")]),
|
||||
},
|
||||
Example {
|
||||
description: "Print the value of the special '$nu' variable",
|
||||
example: "echo $nu",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_value_to_stream(v: Value) -> InputStream {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Table(table),
|
||||
..
|
||||
} => InputStream::from_stream(table.into_iter()),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Range(range)),
|
||||
tag,
|
||||
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
|
||||
x => InputStream::one(x),
|
||||
}
|
||||
}
|
||||
|
||||
fn echo(args: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let stream = rest.into_iter().map(|i| match i.as_string() {
|
||||
Ok(s) => InputStream::one(UntaggedValue::string(s).into_value(i.tag)),
|
||||
_ => expand_value_to_stream(i),
|
||||
});
|
||||
|
||||
Ok(InputStream::from_stream(stream.flatten()))
|
||||
}
|
||||
|
||||
struct RangeIterator {
|
||||
curr: UntaggedValue,
|
||||
end: UntaggedValue,
|
||||
tag: Tag,
|
||||
is_end_inclusive: bool,
|
||||
moves_up: bool,
|
||||
one: UntaggedValue,
|
||||
negative_one: UntaggedValue,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl RangeIterator {
|
||||
pub fn new(range: Range, tag: Tag) -> RangeIterator {
|
||||
let start = match range.from.0.item {
|
||||
Primitive::Nothing => Primitive::Int(0.into()),
|
||||
x => x,
|
||||
};
|
||||
|
||||
let end = match range.to.0.item {
|
||||
Primitive::Nothing => Primitive::Int(i64::MAX),
|
||||
x => x,
|
||||
};
|
||||
|
||||
RangeIterator {
|
||||
moves_up: start <= end,
|
||||
curr: UntaggedValue::Primitive(start),
|
||||
end: UntaggedValue::Primitive(end),
|
||||
tag,
|
||||
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
|
||||
one: UntaggedValue::int(1),
|
||||
negative_one: UntaggedValue::int(-1),
|
||||
done: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for RangeIterator {
|
||||
type Item = Value;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
use std::cmp::Ordering;
|
||||
if self.done {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ordering = if self.end == UntaggedValue::Primitive(Primitive::Nothing) {
|
||||
Ordering::Less
|
||||
} else {
|
||||
match (&self.curr, &self.end) {
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Int(x)),
|
||||
UntaggedValue::Primitive(Primitive::Int(y)),
|
||||
) => x.cmp(y),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Decimal(x)),
|
||||
UntaggedValue::Primitive(Primitive::Decimal(y)),
|
||||
) => x.cmp(y),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Decimal(x)),
|
||||
UntaggedValue::Primitive(Primitive::Int(y)),
|
||||
) => x.cmp(&(BigDecimal::from(*y))),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Int(x)),
|
||||
UntaggedValue::Primitive(Primitive::Decimal(y)),
|
||||
) => (BigDecimal::from(*x)).cmp(y),
|
||||
_ => {
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Cannot create range",
|
||||
"unsupported range",
|
||||
self.tag.span,
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if self.moves_up
|
||||
&& (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal)
|
||||
{
|
||||
let next_value = nu_data::value::compute_values(Operator::Plus, &self.curr, &self.one);
|
||||
|
||||
let mut next = match next_value {
|
||||
Ok(result) => result,
|
||||
|
||||
Err((left_type, right_type)) => {
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
};
|
||||
std::mem::swap(&mut self.curr, &mut next);
|
||||
|
||||
Some(next.into_value(self.tag.clone()))
|
||||
} else if !self.moves_up
|
||||
&& (ordering == Ordering::Greater
|
||||
|| self.is_end_inclusive && ordering == Ordering::Equal)
|
||||
{
|
||||
let next_value =
|
||||
nu_data::value::compute_values(Operator::Plus, &self.curr, &self.negative_one);
|
||||
|
||||
let mut next = match next_value {
|
||||
Ok(result) => result,
|
||||
Err((left_type, right_type)) => {
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
};
|
||||
std::mem::swap(&mut self.curr, &mut next);
|
||||
|
||||
Some(next.into_value(self.tag.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Echo;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Echo {})
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"error make"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("error make")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create an error."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
|
||||
Ok(input
|
||||
.map(|value| {
|
||||
make_error(&value)
|
||||
.map(|err| UntaggedValue::Error(err).into_value(value.tag()))
|
||||
.unwrap_or_else(|| {
|
||||
UntaggedValue::Error(ShellError::untagged_runtime_error(
|
||||
"Creating error value not supported.",
|
||||
))
|
||||
.into_value(value.tag())
|
||||
})
|
||||
})
|
||||
.into_output_stream())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Creates a labeled error",
|
||||
example: r#"[
|
||||
[ msg, labels, span];
|
||||
["The message", "Helpful message here", ([[start, end]; [0, 141]])]
|
||||
] | error make"#,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn make_error(value: &Value) -> Option<ShellError> {
|
||||
if let Value {
|
||||
value: UntaggedValue::Row(dict),
|
||||
..
|
||||
} = value
|
||||
{
|
||||
let msg = dict.get_data_by_key("msg".spanned_unknown());
|
||||
|
||||
let labels =
|
||||
dict.get_data_by_key("labels".spanned_unknown())
|
||||
.and_then(|table| match &table.value {
|
||||
UntaggedValue::Table(_) => table
|
||||
.table_entries()
|
||||
.map(|value| value.as_string().ok())
|
||||
.collect(),
|
||||
UntaggedValue::Primitive(Primitive::String(label)) => {
|
||||
Some(vec![label.to_string()])
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let _anchor = dict.get_data_by_key("tag".spanned_unknown());
|
||||
let span = dict.get_data_by_key("span".spanned_unknown());
|
||||
|
||||
if msg.is_none() || labels.is_none() || span.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let msg = msg.and_then(|msg| msg.as_string().ok());
|
||||
|
||||
if let Some(labels) = labels {
|
||||
if labels.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(ShellError::labeled_error(
|
||||
msg.expect("Message will always be present."),
|
||||
&labels[0],
|
||||
span.map(|data| match data {
|
||||
Value {
|
||||
value: UntaggedValue::Row(vals),
|
||||
..
|
||||
} => match (vals.entries.get("start"), vals.entries.get("end")) {
|
||||
(Some(start), Some(end)) => {
|
||||
let start = start.as_usize().ok().unwrap_or(0);
|
||||
let end = end.as_usize().ok().unwrap_or(0);
|
||||
|
||||
Span::new(start, end)
|
||||
}
|
||||
(_, _) => Span::unknown(),
|
||||
},
|
||||
_ => Span::unknown(),
|
||||
})
|
||||
.unwrap_or_else(Span::unknown),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
mod make;
|
||||
|
||||
pub use make::SubCommand as ErrorMake;
|
@ -1,110 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Find;
|
||||
|
||||
impl WholeStreamCommand for Find {
|
||||
fn name(&self) -> &str {
|
||||
"find"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("find").rest("rest", SyntaxShape::String, "search term")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find text in the output of a previous command"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
find(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Search pipeline output for multiple terms",
|
||||
example: r#"ls | find toml md sh"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Search strings for term(s)",
|
||||
example: r#"echo Cargo.toml | find toml"#,
|
||||
result: Some(vec![Value::from("Cargo.toml")]),
|
||||
},
|
||||
Example {
|
||||
description: "Search a number list for term(s)",
|
||||
example: r#"[1 2 3 4 5] | find 5"#,
|
||||
result: Some(vec![UntaggedValue::int(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Search string list for term(s)",
|
||||
example: r#"[moe larry curly] | find l"#,
|
||||
result: Some(vec![Value::from("larry"), Value::from("curly")]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn row_contains(row: &Dictionary, search_terms: Vec<String>) -> bool {
|
||||
for term in search_terms {
|
||||
for (k, v) in &row.entries {
|
||||
let key = k.to_string().trim().to_lowercase();
|
||||
let value = v.convert_to_string().trim().to_lowercase();
|
||||
if key.contains(&term) || value.contains(&term) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn find(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.filter(move |row| match &row.value {
|
||||
UntaggedValue::Row(row) => {
|
||||
let sterms: Vec<String> = rest
|
||||
.iter()
|
||||
.map(|t| t.convert_to_string().trim().to_lowercase())
|
||||
.collect();
|
||||
row_contains(row, sterms)
|
||||
}
|
||||
UntaggedValue::Primitive(_p) => {
|
||||
// eprint!("prim {}", p.type_name());
|
||||
let sterms: Vec<String> = rest
|
||||
.iter()
|
||||
.map(|t| t.convert_to_string().trim().to_lowercase())
|
||||
.collect();
|
||||
|
||||
let prim_string = &row.convert_to_string().trim().to_lowercase();
|
||||
for term in sterms {
|
||||
if prim_string.contains(&term) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.into_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Find;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Find {})
|
||||
}
|
||||
}
|
@ -1,353 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::TaggedListBuilder;
|
||||
use nu_engine::{documentation::generate_docs, Command, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
Dictionary, NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape,
|
||||
TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::{SpannedItem, Tag, Tagged};
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
pub struct Help;
|
||||
|
||||
impl WholeStreamCommand for Help {
|
||||
fn name(&self) -> &str {
|
||||
"help"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("help")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::String,
|
||||
"the name of command to get help on",
|
||||
)
|
||||
.named(
|
||||
"find",
|
||||
SyntaxShape::String,
|
||||
"string to find in command usage",
|
||||
Some('f'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Display help information about commands."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
help(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "show all commands and sub-commands",
|
||||
example: "help commands",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "generate documentation",
|
||||
example: "help generate_docs",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "show help for single command",
|
||||
example: "help match",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "show help for single sub-command",
|
||||
example: "help str lpad",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "search for string in command usage",
|
||||
example: "help --find char",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn help(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let scope = args.scope().clone();
|
||||
let find: Option<Tagged<String>> = args.get_flag("find")?;
|
||||
let rest: Vec<Tagged<String>> = args.rest(0)?;
|
||||
|
||||
if let Some(f) = find {
|
||||
let search_string = f.item;
|
||||
let full_commands = scope.get_commands_info();
|
||||
let mut found_cmds_vec = Vec::new();
|
||||
|
||||
for (key, cmd) in full_commands {
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
||||
let c = cmd.usage().to_string();
|
||||
let e = cmd.extra_usage().to_string();
|
||||
if key.to_lowercase().contains(&search_string)
|
||||
|| c.to_lowercase().contains(&search_string)
|
||||
|| e.to_lowercase().contains(&search_string)
|
||||
{
|
||||
indexmap.insert(
|
||||
"name".to_string(),
|
||||
UntaggedValue::string(key).into_value(&name),
|
||||
);
|
||||
|
||||
indexmap.insert(
|
||||
"usage".to_string(),
|
||||
UntaggedValue::string(cmd.usage().to_string()).into_value(&name),
|
||||
);
|
||||
|
||||
indexmap.insert(
|
||||
"extra_usage".to_string(),
|
||||
UntaggedValue::string(cmd.extra_usage().to_string()).into_value(&name),
|
||||
);
|
||||
|
||||
found_cmds_vec
|
||||
.push(UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&name));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(found_cmds_vec.into_iter().into_action_stream());
|
||||
}
|
||||
|
||||
if !rest.is_empty() {
|
||||
if rest[0].item == "commands" {
|
||||
let mut sorted_names = scope.get_command_names();
|
||||
sorted_names.sort();
|
||||
|
||||
let (mut subcommand_names, command_names) = sorted_names
|
||||
.into_iter()
|
||||
// private only commands shouldn't be displayed
|
||||
.filter(|cmd_name| {
|
||||
scope
|
||||
.get_command(cmd_name)
|
||||
.filter(|command| !command.is_private())
|
||||
.is_some()
|
||||
})
|
||||
.partition::<Vec<_>, _>(|cmd_name| cmd_name.contains(' '));
|
||||
|
||||
fn process_name(
|
||||
dict: &mut TaggedDictBuilder,
|
||||
cmd_name: &str,
|
||||
scope: Scope,
|
||||
rest: Vec<Tagged<String>>,
|
||||
name: Tag,
|
||||
) -> Result<(), ShellError> {
|
||||
let document_tag = rest[0].tag.clone();
|
||||
let value = command_dict(
|
||||
scope.get_command(cmd_name).ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
format!("Could not load {}", cmd_name),
|
||||
"could not load command",
|
||||
document_tag,
|
||||
)
|
||||
})?,
|
||||
name,
|
||||
);
|
||||
|
||||
dict.insert_untagged("name", cmd_name);
|
||||
dict.insert_untagged(
|
||||
"description",
|
||||
value
|
||||
.get_data_by_key("usage".spanned_unknown())
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
"Expected a usage key",
|
||||
"expected a 'usage' key",
|
||||
&value.tag,
|
||||
)
|
||||
})?
|
||||
.as_string()?,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_subcommands_table(
|
||||
subcommand_names: &mut Vec<String>,
|
||||
cmd_name: &str,
|
||||
scope: Scope,
|
||||
rest: Vec<Tagged<String>>,
|
||||
name: Tag,
|
||||
) -> Result<Value, ShellError> {
|
||||
let (matching, not_matching) =
|
||||
subcommand_names.drain(..).partition(|subcommand_name| {
|
||||
subcommand_name.starts_with(&format!("{} ", cmd_name))
|
||||
});
|
||||
*subcommand_names = not_matching;
|
||||
Ok(if !matching.is_empty() {
|
||||
UntaggedValue::table(
|
||||
&(matching
|
||||
.into_iter()
|
||||
.map(|cmd_name: String| -> Result<_, ShellError> {
|
||||
let mut short_desc = TaggedDictBuilder::new(name.clone());
|
||||
process_name(
|
||||
&mut short_desc,
|
||||
&cmd_name,
|
||||
scope.clone(),
|
||||
rest.clone(),
|
||||
name.clone(),
|
||||
)?;
|
||||
Ok(short_desc.into_value())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?[..]),
|
||||
)
|
||||
.into_value(name)
|
||||
} else {
|
||||
UntaggedValue::nothing().into_value(name)
|
||||
})
|
||||
}
|
||||
|
||||
let iterator =
|
||||
command_names
|
||||
.into_iter()
|
||||
.map(move |cmd_name| -> Result<_, ShellError> {
|
||||
let mut short_desc = TaggedDictBuilder::new(name.clone());
|
||||
process_name(
|
||||
&mut short_desc,
|
||||
&cmd_name,
|
||||
scope.clone(),
|
||||
rest.clone(),
|
||||
name.clone(),
|
||||
)?;
|
||||
short_desc.insert_value(
|
||||
"subcommands",
|
||||
make_subcommands_table(
|
||||
&mut subcommand_names,
|
||||
&cmd_name,
|
||||
scope.clone(),
|
||||
rest.clone(),
|
||||
name.clone(),
|
||||
)?,
|
||||
);
|
||||
ReturnSuccess::value(short_desc.into_value())
|
||||
});
|
||||
|
||||
Ok(iterator.into_action_stream())
|
||||
} else if rest[0].item == "generate_docs" {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(generate_docs(
|
||||
&scope,
|
||||
))))
|
||||
} else if rest.len() == 2 {
|
||||
// Check for a subcommand
|
||||
let command_name = format!("{} {}", rest[0].item, rest[1].item);
|
||||
if let Some(command) = scope.get_command(&command_name) {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(command.stream_command(), &scope))
|
||||
.into_value(Tag::unknown()),
|
||||
)))
|
||||
} else {
|
||||
Ok(ActionStream::empty())
|
||||
}
|
||||
} else if let Some(command) = scope.get_command(&rest[0].item) {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(command.stream_command(), &scope))
|
||||
.into_value(Tag::unknown()),
|
||||
)))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Can't find command (use 'help commands' for full list)",
|
||||
"can't find command",
|
||||
rest[0].tag.span,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
let msg = r#"Welcome to Nushell.
|
||||
|
||||
Here are some tips to help you get started.
|
||||
* help commands - list all available commands
|
||||
* help <command name> - display help about a particular command
|
||||
|
||||
Nushell works on the idea of a "pipeline". Pipelines are commands connected with the '|' character.
|
||||
Each stage in the pipeline works together to load, parse, and display information to you.
|
||||
|
||||
[Examples]
|
||||
|
||||
List the files in the current directory, sorted by size:
|
||||
ls | sort-by size
|
||||
|
||||
Get information about the current system:
|
||||
sys | get host
|
||||
|
||||
Get the processes on your system actively using CPU:
|
||||
ps | where cpu > 0
|
||||
|
||||
You can also learn more at https://www.nushell.sh/book/"#;
|
||||
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(msg).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
fn for_spec(name: &str, ty: &str, required: bool, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
|
||||
let mut spec = TaggedDictBuilder::new(tag);
|
||||
|
||||
spec.insert_untagged("name", UntaggedValue::string(name));
|
||||
spec.insert_untagged("type", UntaggedValue::string(ty));
|
||||
spec.insert_untagged(
|
||||
"required",
|
||||
UntaggedValue::string(if required { "yes" } else { "no" }),
|
||||
);
|
||||
|
||||
spec.into_value()
|
||||
}
|
||||
|
||||
pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let mut sig = TaggedListBuilder::new(&tag);
|
||||
|
||||
for arg in &signature.positional {
|
||||
let is_required = matches!(arg.0, PositionalType::Mandatory(_, _));
|
||||
|
||||
sig.push_value(for_spec(arg.0.name(), "argument", is_required, &tag));
|
||||
}
|
||||
|
||||
if signature.rest_positional.is_some() {
|
||||
let is_required = false;
|
||||
sig.push_value(for_spec("rest", "argument", is_required, &tag));
|
||||
}
|
||||
|
||||
for (name, ty) in &signature.named {
|
||||
match ty.0 {
|
||||
NamedType::Mandatory(_, _) => sig.push_value(for_spec(name, "flag", true, &tag)),
|
||||
NamedType::Optional(_, _) => sig.push_value(for_spec(name, "flag", false, &tag)),
|
||||
NamedType::Switch(_) => sig.push_value(for_spec(name, "switch", false, &tag)),
|
||||
}
|
||||
}
|
||||
|
||||
sig.into_value()
|
||||
}
|
||||
|
||||
fn command_dict(command: Command, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
|
||||
let mut cmd_dict = TaggedDictBuilder::new(&tag);
|
||||
|
||||
cmd_dict.insert_untagged("name", UntaggedValue::string(command.name()));
|
||||
|
||||
cmd_dict.insert_untagged("type", UntaggedValue::string("Command"));
|
||||
|
||||
cmd_dict.insert_value("signature", signature_dict(command.signature(), tag));
|
||||
cmd_dict.insert_untagged("usage", UntaggedValue::string(command.usage()));
|
||||
|
||||
cmd_dict.into_value()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Help;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Help {})
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
pub struct History;
|
||||
|
||||
impl WholeStreamCommand for History {
|
||||
fn name(&self) -> &str {
|
||||
"history"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("history").switch("clear", "Clears out the history entries", Some('c'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Display command history."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
history(args)
|
||||
}
|
||||
}
|
||||
|
||||
fn history(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctx = &args.context;
|
||||
|
||||
let clear = args.has_flag("clear");
|
||||
|
||||
let path = if let Some(global_cfg) = &ctx.configs().lock().global_config {
|
||||
nu_data::config::path::history_path_or_default(global_cfg)
|
||||
} else {
|
||||
nu_data::config::path::default_history_path()
|
||||
};
|
||||
|
||||
if clear {
|
||||
// This is a NOOP, the logic to clear is handled in cli.rs
|
||||
Ok(ActionStream::empty())
|
||||
} else if let Ok(file) = File::open(path) {
|
||||
let reader = BufReader::new(file);
|
||||
// Skips the first line, which is a Rustyline internal
|
||||
let output = reader.lines().skip(1).filter_map(move |line| match line {
|
||||
Ok(line) => Some(ReturnSuccess::value(
|
||||
UntaggedValue::string(line).into_value(tag.clone()),
|
||||
)),
|
||||
Err(_) => None,
|
||||
});
|
||||
|
||||
Ok(output.into_action_stream())
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Could not open history",
|
||||
"history file could not be opened",
|
||||
tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::History;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(History {})
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_stream::OutputStream;
|
||||
|
||||
pub struct If;
|
||||
|
||||
impl WholeStreamCommand for If {
|
||||
fn name(&self) -> &str {
|
||||
"if"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("if")
|
||||
.required(
|
||||
"condition",
|
||||
SyntaxShape::MathExpression,
|
||||
"the condition that must match",
|
||||
)
|
||||
.required(
|
||||
"then_case",
|
||||
SyntaxShape::Block,
|
||||
"block to run if condition is true",
|
||||
)
|
||||
.required(
|
||||
"else_case",
|
||||
SyntaxShape::Block,
|
||||
"block to run if condition is false",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Run blocks if a condition is true or false."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if_command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Run a block if a condition is true",
|
||||
example: "let x = 10; if $x > 5 { echo 'greater than 5' } { echo 'less than or equal to 5' }",
|
||||
result: Some(vec![UntaggedValue::string("greater than 5").into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Run a block if a condition is false",
|
||||
example: "let x = 1; if $x > 5 { echo 'greater than 5' } { echo 'less than or equal to 5' }",
|
||||
result: Some(vec![UntaggedValue::string("less than or equal to 5").into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
fn if_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let external_redirection = args.call_info.args.external_redirection;
|
||||
let context = Arc::new(args.context.clone());
|
||||
|
||||
let condition: CapturedBlock = args.req(0)?;
|
||||
let then_case: CapturedBlock = args.req(1)?;
|
||||
let else_case: CapturedBlock = args.req(2)?;
|
||||
let input = args.input;
|
||||
|
||||
let cond = {
|
||||
if condition.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match condition.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => expr,
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
context.scope.enter_scope();
|
||||
context.scope.add_vars(&condition.captured.entries);
|
||||
|
||||
//FIXME: should we use the scope that's brought in as well?
|
||||
let condition = evaluate_baseline_expr(cond, &context);
|
||||
let result = match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => {
|
||||
if b {
|
||||
run_block(&then_case.block, &context, input, external_redirection)
|
||||
} else {
|
||||
run_block(&else_case.block, &context, input, external_redirection)
|
||||
}
|
||||
}
|
||||
Err(e) => Ok(OutputStream::from_stream(
|
||||
vec![UntaggedValue::Error(e).into_untagged_value()].into_iter(),
|
||||
)),
|
||||
},
|
||||
Err(e) => Ok(OutputStream::from_stream(
|
||||
vec![UntaggedValue::Error(e).into_untagged_value()].into_iter(),
|
||||
)),
|
||||
};
|
||||
context.scope.exit_scope();
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::If;
|
||||
use super::ShellError;
|
||||
use nu_test_support::nu;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(If {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_doesnt_leak_on_error() {
|
||||
let actual = nu!(
|
||||
".",
|
||||
r#"
|
||||
def test-leak [] {
|
||||
let var = "hello"
|
||||
if 0 == "" {echo ok} {echo not}
|
||||
}
|
||||
test-leak
|
||||
echo $var
|
||||
"#
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("unknown variable"));
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
extern crate unicode_segmentation;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::Signature;
|
||||
|
||||
pub struct Ignore;
|
||||
|
||||
impl WholeStreamCommand for Ignore {
|
||||
fn name(&self) -> &str {
|
||||
"ignore"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("ignore")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Ignore the output of the previous command in the pipeline"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let _: Vec<_> = args.input.collect();
|
||||
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Ignore the output of an echo command",
|
||||
example: r#"echo done | ignore"#,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ignore;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Ignore {})
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::{evaluate_baseline_expr, FromValue, WholeStreamCommand};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::{CapturedBlock, ClassifiedCommand},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct Let;
|
||||
|
||||
impl WholeStreamCommand for Let {
|
||||
fn name(&self) -> &str {
|
||||
"let"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("let")
|
||||
.required("name", SyntaxShape::String, "the name of the variable")
|
||||
.required("equals", SyntaxShape::String, "the equals sign")
|
||||
.required(
|
||||
"expr",
|
||||
SyntaxShape::MathExpression,
|
||||
"the value for the variable",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create a variable and give it a value."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
letcmd(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Assign a simple value to a variable",
|
||||
example: "let x = 3",
|
||||
result: Some(vec![]),
|
||||
},
|
||||
Example {
|
||||
description: "Assign the result of an expression to a variable",
|
||||
example: "let result = (3 + 7); echo $result",
|
||||
result: Some(vec![UntaggedValue::int(1).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Create a variable using the full name",
|
||||
example: "let $three = 3",
|
||||
result: Some(vec![]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = &args.context;
|
||||
let positional = args
|
||||
.call_info
|
||||
.args
|
||||
.positional
|
||||
.expect("Internal error: type checker should require args");
|
||||
|
||||
let var_name = positional[0].var_name()?;
|
||||
let rhs_raw = evaluate_baseline_expr(&positional[2], ctx)?;
|
||||
let tag: Tag = positional[2].span.into();
|
||||
|
||||
let rhs: CapturedBlock = FromValue::from_value(&rhs_raw)?;
|
||||
|
||||
let (expr, _) = {
|
||||
if rhs.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a value",
|
||||
"expected a value",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match rhs.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => (expr, &rhs.captured),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a value",
|
||||
"expected a value",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a value",
|
||||
"expected a value",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ctx.scope.enter_scope();
|
||||
let value = evaluate_baseline_expr(expr, ctx);
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
let value = value?;
|
||||
|
||||
// Note: this is a special case for setting the context from a command
|
||||
// In this case, if we don't set it now, we'll lose the scope that this
|
||||
// variable should be set into.
|
||||
ctx.scope.add_var(var_name, value);
|
||||
|
||||
Ok(ActionStream::empty())
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
mod alias;
|
||||
mod debug;
|
||||
mod def;
|
||||
mod describe;
|
||||
mod do_;
|
||||
pub(crate) mod echo;
|
||||
mod error;
|
||||
mod find;
|
||||
mod help;
|
||||
mod history;
|
||||
mod if_;
|
||||
mod ignore;
|
||||
mod let_;
|
||||
mod nu_plugin;
|
||||
mod nu_signature;
|
||||
mod source;
|
||||
mod tags;
|
||||
mod tutor;
|
||||
mod unalias;
|
||||
mod version;
|
||||
|
||||
pub use self::nu_plugin::SubCommand as NuPlugin;
|
||||
pub use self::nu_signature::{
|
||||
loglevels, testbins, version as core_version, Command as NuSignature,
|
||||
};
|
||||
pub use alias::Alias;
|
||||
pub use debug::Debug;
|
||||
pub use def::Def;
|
||||
pub use describe::Describe;
|
||||
pub use do_::Do;
|
||||
pub use echo::Echo;
|
||||
pub use error::*;
|
||||
pub use find::Find;
|
||||
pub use help::Help;
|
||||
pub use history::History;
|
||||
pub use if_::If;
|
||||
pub use ignore::Ignore;
|
||||
pub use let_::Let;
|
||||
pub use source::Source;
|
||||
pub use tags::Tags;
|
||||
pub use tutor::Tutor;
|
||||
pub use unalias::Unalias;
|
||||
pub use version::{version, Version};
|
@ -1,120 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
#[serde(rename = "load")]
|
||||
pub load_path: Option<Tagged<PathBuf>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"nu plugin"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("nu plugin").named(
|
||||
"load",
|
||||
SyntaxShape::FilePath,
|
||||
"a path to load the plugins from",
|
||||
Some('l'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Nu Plugin"
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Load all plugins in the current directory",
|
||||
example: "nu plugin --load .",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let scope = args.scope().clone();
|
||||
let shell_manager = args.shell_manager();
|
||||
|
||||
let load_path: Option<Tagged<PathBuf>> = args.get_flag("load")?;
|
||||
|
||||
if let Some(Tagged {
|
||||
item: load_path,
|
||||
tag,
|
||||
}) = load_path
|
||||
{
|
||||
let path = canonicalize_with(load_path, shell_manager.path()).map_err(|_| {
|
||||
ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
"directory not found",
|
||||
&tag,
|
||||
)
|
||||
})?;
|
||||
|
||||
if !path.is_dir() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
"is not a directory",
|
||||
&tag,
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let has_exec = path
|
||||
.metadata()
|
||||
.map(|m| umask::Mode::from(m.permissions().mode()).has(umask::USER_READ))
|
||||
.map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
format!("cannot stat ({})", e),
|
||||
&tag,
|
||||
)
|
||||
})?;
|
||||
|
||||
if !has_exec {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
"permission denied",
|
||||
&tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(vec![ReturnSuccess::action(CommandAction::AddPlugins(
|
||||
path.to_string_lossy().to_string(),
|
||||
))]
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&SubCommand, &scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"nu"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("nu")
|
||||
.switch("version", "Display Nu version", Some('v'))
|
||||
.switch("stdin", "redirect stdin", None)
|
||||
.switch("skip-plugins", "do not load plugins", None)
|
||||
.switch("no-history", "don't save history", None)
|
||||
.switch("perf", "show startup performance metrics", None)
|
||||
.switch("login", "start Nu as if it was a login shell", Some('l'))
|
||||
.named(
|
||||
"commands",
|
||||
SyntaxShape::String,
|
||||
"commands to run",
|
||||
Some('c'),
|
||||
)
|
||||
.named(
|
||||
"testbin",
|
||||
SyntaxShape::String,
|
||||
"test bin: echo_env, cococo, iecho, fail, nonu, chop, repeater, meow",
|
||||
None,
|
||||
)
|
||||
.named("develop", SyntaxShape::String, "trace mode", None)
|
||||
.named("debug", SyntaxShape::String, "debug mode", None)
|
||||
.named(
|
||||
"loglevel",
|
||||
SyntaxShape::String,
|
||||
"LEVEL: error, warn, info, debug, trace",
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"config-file",
|
||||
SyntaxShape::FilePath,
|
||||
"custom configuration source file",
|
||||
None,
|
||||
)
|
||||
.rest("rest", SyntaxShape::String, "source file(s) to run")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Nu - A new type of shell."
|
||||
}
|
||||
}
|
||||
|
||||
pub fn version() -> &'static str {
|
||||
env!("CARGO_PKG_VERSION")
|
||||
}
|
||||
|
||||
pub fn testbins() -> Vec<String> {
|
||||
vec![
|
||||
"echo_env", "cococo", "iecho", "fail", "nonu", "chop", "repeater", "meow",
|
||||
]
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn loglevels() -> Vec<String> {
|
||||
vec!["error", "warn", "info", "debug", "trace"]
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect()
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::{script, WholeStreamCommand};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_path::{canonicalize, canonicalize_with};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
pub struct Source;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SourceArgs {
|
||||
pub filename: Tagged<String>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Source {
|
||||
fn name(&self) -> &str {
|
||||
"source"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("source").required(
|
||||
"filename",
|
||||
SyntaxShape::FilePath,
|
||||
"the filepath to the script file to source",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Runs a script file in the current context."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
source(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = &args.context;
|
||||
let filename: Tagged<String> = args.req(0)?;
|
||||
|
||||
let source_file = Path::new(&filename.item);
|
||||
|
||||
// Note: this is a special case for setting the context from a command
|
||||
// In this case, if we don't set it now, we'll lose the scope that this
|
||||
// variable should be set into.
|
||||
|
||||
let lib_dirs = &ctx
|
||||
.configs()
|
||||
.lock()
|
||||
.global_config
|
||||
.as_ref()
|
||||
.map(|configuration| match configuration.var("lib_dirs") {
|
||||
Some(paths) => paths
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.map(|path| path.as_string())
|
||||
.collect(),
|
||||
None => vec![],
|
||||
});
|
||||
|
||||
if let Some(dir) = lib_dirs {
|
||||
for lib_path in dir {
|
||||
match lib_path {
|
||||
Ok(name) => {
|
||||
let path = if let Ok(p) = canonicalize_with(&source_file, name) {
|
||||
p
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Ok(contents) = std::fs::read_to_string(path) {
|
||||
let result = script::run_script_standalone(contents, true, ctx, false);
|
||||
|
||||
if let Err(err) = result {
|
||||
ctx.error(err);
|
||||
}
|
||||
return Ok(OutputStream::empty());
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
ctx.error(reason.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let path = canonicalize(source_file).map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
format!("Can't load source file. Reason: {}", e),
|
||||
"Can't load this file",
|
||||
filename.span(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let contents = std::fs::read_to_string(path);
|
||||
|
||||
match contents {
|
||||
Ok(contents) => {
|
||||
let result = script::run_script_standalone(contents, true, ctx, false);
|
||||
|
||||
if let Err(err) = result {
|
||||
ctx.error(err);
|
||||
}
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
Err(e) => {
|
||||
ctx.error(ShellError::labeled_error(
|
||||
format!("Can't load source file. Reason: {}", e),
|
||||
"Can't load this file",
|
||||
filename.span(),
|
||||
));
|
||||
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct Tags;
|
||||
|
||||
impl WholeStreamCommand for Tags {
|
||||
fn name(&self) -> &str {
|
||||
"tags"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("tags")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Read the tags (metadata) for values."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(tags(args))
|
||||
}
|
||||
}
|
||||
|
||||
fn build_tag_table(tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
TaggedDictBuilder::build(tag.clone(), |tags| {
|
||||
if let Some(anchor) = anchor_as_value(&tag) {
|
||||
tags.insert_value("anchor", anchor);
|
||||
}
|
||||
|
||||
tags.insert_value(
|
||||
"span",
|
||||
TaggedDictBuilder::build(tag.clone(), |span_dict| {
|
||||
span_dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
|
||||
span_dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
|
||||
}),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
fn tags(args: CommandArgs) -> OutputStream {
|
||||
if args.input.is_empty() {
|
||||
OutputStream::one(build_tag_table(&args.name_tag()))
|
||||
} else {
|
||||
args.input
|
||||
.map(move |v| build_tag_table(v.tag()))
|
||||
.into_output_stream()
|
||||
}
|
||||
}
|
||||
|
||||
fn anchor_as_value(tag: &Tag) -> Option<Value> {
|
||||
let anchor = tag.anchor.as_ref();
|
||||
|
||||
anchor.as_ref()?;
|
||||
|
||||
Some(TaggedDictBuilder::build(tag, |table| {
|
||||
let value = match anchor {
|
||||
Some(AnchorLocation::File(path)) => {
|
||||
Some(("file", UntaggedValue::from(path.to_string())))
|
||||
}
|
||||
Some(AnchorLocation::Url(destination)) => {
|
||||
Some(("url", UntaggedValue::from(destination.to_string())))
|
||||
}
|
||||
Some(AnchorLocation::Source(text)) => Some((
|
||||
"source",
|
||||
UntaggedValue::Primitive(Primitive::String(text.to_string())),
|
||||
)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
if let Some((key, value)) = value {
|
||||
table.insert_untagged(key, value);
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::Tags;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Tags {})
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
|
||||
pub struct Unalias;
|
||||
|
||||
impl WholeStreamCommand for Unalias {
|
||||
fn name(&self) -> &str {
|
||||
"unalias"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("unalias").required("name", SyntaxShape::String, "the name of the alias")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Removes an alias"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
unalias(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Remove the 'v' alias",
|
||||
example: "unalias v",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unalias(_: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::empty())
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, FrameStruct, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::{frame::groupby::GroupBy, prelude::PolarsError};
|
||||
|
||||
enum Operation {
|
||||
Mean,
|
||||
Sum,
|
||||
Min,
|
||||
Max,
|
||||
First,
|
||||
Last,
|
||||
Nunique,
|
||||
Quantile(f64),
|
||||
Median,
|
||||
Var,
|
||||
Std,
|
||||
Count,
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
fn from_tagged(
|
||||
name: &Tagged<String>,
|
||||
quantile: Option<Tagged<f64>>,
|
||||
) -> Result<Operation, ShellError> {
|
||||
match name.item.as_ref() {
|
||||
"mean" => Ok(Operation::Mean),
|
||||
"sum" => Ok(Operation::Sum),
|
||||
"min" => Ok(Operation::Min),
|
||||
"max" => Ok(Operation::Max),
|
||||
"first" => Ok(Operation::First),
|
||||
"last" => Ok(Operation::Last),
|
||||
"nunique" => Ok(Operation::Nunique),
|
||||
"quantile" => {
|
||||
match quantile {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Quantile value not fount",
|
||||
"Quantile operation requires quantile value",
|
||||
&name.tag,
|
||||
)),
|
||||
Some(value ) => {
|
||||
if (value.item < 0.0) | (value.item > 1.0) {
|
||||
Err(ShellError::labeled_error(
|
||||
"Inappropriate quantile",
|
||||
"Quantile value should be between 0.0 and 1.0",
|
||||
&value.tag,
|
||||
))
|
||||
} else {
|
||||
Ok(Operation::Quantile(value.item))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"median" => Ok(Operation::Median),
|
||||
"var" => Ok(Operation::Var),
|
||||
"std" => Ok(Operation::Std),
|
||||
"count" => Ok(Operation::Count),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Operation not fount",
|
||||
"Operation does not exist",
|
||||
&name.tag,
|
||||
"Perhaps you want: mean, sum, min, max, first, last, nunique, quantile, median, var, std, or count",
|
||||
&name.tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe aggregate"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame, GroupBy, Series] Performs an aggregation operation on a dataframe, groupby or series object"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe aggregate")
|
||||
.required("operation", SyntaxShape::String, "aggregate operation")
|
||||
.named(
|
||||
"quantile",
|
||||
SyntaxShape::Number,
|
||||
"quantile value for quantile operation",
|
||||
Some('q'),
|
||||
)
|
||||
.switch(
|
||||
"explicit",
|
||||
"returns explicit names for groupby aggregations",
|
||||
Some('e'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Aggregate sum by grouping by column a and summing on col b",
|
||||
example:
|
||||
"[[a b]; [one 1] [one 2]] | dataframe to-df | dataframe group-by a | dataframe aggregate sum",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::string("one").into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(3).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Aggregate sum in dataframe columns",
|
||||
example: "[[a b]; [4 1] [5 2]] | dataframe to-df | dataframe aggregate sum",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::int(9).into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(3).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Aggregate sum in series",
|
||||
example: "[4 1 5 6] | dataframe to-df | dataframe aggregate sum",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("0".to_string(), vec![UntaggedValue::int(16).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let quantile: Option<Tagged<f64>> = args.get_flag("quantile")?;
|
||||
let operation: Tagged<String> = args.req(0)?;
|
||||
let op = Operation::from_tagged(&operation, quantile)?;
|
||||
|
||||
let value = args.input.next().ok_or_else(|| {
|
||||
ShellError::labeled_error("Empty stream", "No value found in the stream", &tag)
|
||||
})?;
|
||||
|
||||
match value.value {
|
||||
UntaggedValue::FrameStruct(FrameStruct::GroupBy(nu_groupby)) => {
|
||||
let groupby = nu_groupby.to_groupby()?;
|
||||
|
||||
let res = perform_groupby_aggregation(
|
||||
groupby,
|
||||
op,
|
||||
&operation.tag,
|
||||
&tag.span,
|
||||
args.has_flag("explicit"),
|
||||
)?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
UntaggedValue::DataFrame(df) => {
|
||||
let df = df.as_ref();
|
||||
|
||||
let res = perform_dataframe_aggregation(df, op, &operation.tag)?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"No groupby, dataframe or series in stream",
|
||||
"no groupby, dataframe or series found in input stream",
|
||||
&value.tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_groupby_aggregation(
|
||||
groupby: GroupBy,
|
||||
operation: Operation,
|
||||
operation_tag: &Tag,
|
||||
agg_span: &Span,
|
||||
explicit: bool,
|
||||
) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
let mut res = match operation {
|
||||
Operation::Mean => groupby.mean(),
|
||||
Operation::Sum => groupby.sum(),
|
||||
Operation::Min => groupby.min(),
|
||||
Operation::Max => groupby.max(),
|
||||
Operation::First => groupby.first(),
|
||||
Operation::Last => groupby.last(),
|
||||
Operation::Nunique => groupby.n_unique(),
|
||||
Operation::Quantile(quantile) => groupby.quantile(quantile),
|
||||
Operation::Median => groupby.median(),
|
||||
Operation::Var => groupby.var(),
|
||||
Operation::Std => groupby.std(),
|
||||
Operation::Count => groupby.count(),
|
||||
}
|
||||
.map_err(|e| {
|
||||
let span = match &e {
|
||||
PolarsError::NotFound(_) => agg_span,
|
||||
_ => &operation_tag.span,
|
||||
};
|
||||
|
||||
parse_polars_error::<&str>(&e, span, None)
|
||||
})?;
|
||||
|
||||
if !explicit {
|
||||
let col_names = res
|
||||
.get_column_names()
|
||||
.iter()
|
||||
.map(|name| name.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
for col in col_names {
|
||||
let from = match operation {
|
||||
Operation::Mean => "_mean",
|
||||
Operation::Sum => "_sum",
|
||||
Operation::Min => "_min",
|
||||
Operation::Max => "_max",
|
||||
Operation::First => "_first",
|
||||
Operation::Last => "_last",
|
||||
Operation::Nunique => "_n_unique",
|
||||
Operation::Quantile(_) => "_quantile",
|
||||
Operation::Median => "_median",
|
||||
Operation::Var => "_agg_var",
|
||||
Operation::Std => "_agg_std",
|
||||
Operation::Count => "_count",
|
||||
};
|
||||
|
||||
let new_col = match col.find(from) {
|
||||
Some(index) => &col[..index],
|
||||
None => &col[..],
|
||||
};
|
||||
|
||||
res.rename(&col, new_col)
|
||||
.expect("Column is always there. Looping with known names");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn perform_dataframe_aggregation(
|
||||
dataframe: &polars::prelude::DataFrame,
|
||||
operation: Operation,
|
||||
operation_tag: &Tag,
|
||||
) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
match operation {
|
||||
Operation::Mean => Ok(dataframe.mean()),
|
||||
Operation::Sum => Ok(dataframe.sum()),
|
||||
Operation::Min => Ok(dataframe.min()),
|
||||
Operation::Max => Ok(dataframe.max()),
|
||||
Operation::Quantile(quantile) => dataframe
|
||||
.quantile(quantile)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &operation_tag.span, None)),
|
||||
Operation::Median => Ok(dataframe.median()),
|
||||
Operation::Var => Ok(dataframe.var()),
|
||||
Operation::Std => Ok(dataframe.std()),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Not valid operation",
|
||||
"operation not valid for dataframe",
|
||||
&operation_tag.span,
|
||||
"Perhaps you want: mean, sum, min, max, quantile, median, var, or std",
|
||||
&operation_tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Axis, Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe append"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Appends a new dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe append")
|
||||
.required_named(
|
||||
"other",
|
||||
SyntaxShape::Any,
|
||||
"dataframe to be appended",
|
||||
Some('o'),
|
||||
)
|
||||
.required_named(
|
||||
"axis",
|
||||
SyntaxShape::String,
|
||||
"row or col axis orientation",
|
||||
Some('a'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Appends a dataframe as new columns",
|
||||
example: r#"let a = ([[a b]; [1 2] [3 4]] | dataframe to-df);
|
||||
$a | dataframe append -o $a -a row"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
Column::new(
|
||||
"a_x".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b_x".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Appends a dataframe merging at the end of columns",
|
||||
example: r#"let a = ([[a b]; [1 2] [3 4]] | dataframe to-df);
|
||||
$a | dataframe append -o $a -a col"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let other: Value = args.req_named("other")?;
|
||||
let axis: Tagged<String> = args.req_named("axis")?;
|
||||
|
||||
let axis = Axis::try_from_str(&axis.item, &axis.tag.span)?;
|
||||
|
||||
let df_other = match other.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only append a dataframe to a dataframe",
|
||||
other.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let df_new = df.append_df(&df_other, axis, &tag.span)?;
|
||||
Ok(OutputStream::one(df_new.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
use super::utils::parse_polars_error;
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe column"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Returns the selected column as Series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe column").required("column", SyntaxShape::String, "column name")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns the selected column as series",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe column a",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let column: Tagged<String> = args.req(0)?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.column(&column.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &column.tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.clone()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Commands to work with polars dataframes"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
|
||||
))
|
||||
}
|
||||
}
|
@ -1,232 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::{
|
||||
chunked_array::ChunkedArray,
|
||||
prelude::{
|
||||
AnyValue, DataFrame as PolarsDF, DataType, Float64Type, IntoSeries, NewChunkedArray,
|
||||
Series, Utf8Type,
|
||||
},
|
||||
};
|
||||
|
||||
use super::utils::parse_polars_error;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe describe"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Describes dataframes numeric columns"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe describe")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Describes dataframe",
|
||||
example: "[[a b]; [1 1] [1 1]] | dataframe to-df | dataframe describe",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"descriptor".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("count").into(),
|
||||
UntaggedValue::string("sum").into(),
|
||||
UntaggedValue::string("mean").into(),
|
||||
UntaggedValue::string("median").into(),
|
||||
UntaggedValue::string("std").into(),
|
||||
UntaggedValue::string("min").into(),
|
||||
UntaggedValue::string("25%").into(),
|
||||
UntaggedValue::string("50%").into(),
|
||||
UntaggedValue::string("75%").into(),
|
||||
UntaggedValue::string("max").into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"a (i64)".to_string(),
|
||||
vec![
|
||||
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(0.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"b (i64)".to_string(),
|
||||
vec![
|
||||
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(0.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let names = ChunkedArray::<Utf8Type>::new_from_opt_slice(
|
||||
"descriptor",
|
||||
&[
|
||||
Some("count"),
|
||||
Some("sum"),
|
||||
Some("mean"),
|
||||
Some("median"),
|
||||
Some("std"),
|
||||
Some("min"),
|
||||
Some("25%"),
|
||||
Some("50%"),
|
||||
Some("75%"),
|
||||
Some("max"),
|
||||
],
|
||||
)
|
||||
.into_series();
|
||||
|
||||
let head = std::iter::once(names);
|
||||
|
||||
let tail = df.as_ref().get_columns().iter().map(|col| {
|
||||
let count = col.len() as f64;
|
||||
|
||||
let sum = match col.sum_as_series().cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let mean = match col.mean_as_series().get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let median = match col.median_as_series().get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let std = match col.std_as_series().get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let min = match col.min_as_series().cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let q_25 = match col.quantile_as_series(0.25) {
|
||||
Ok(ca) => match ca.cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let q_50 = match col.quantile_as_series(0.50) {
|
||||
Ok(ca) => match ca.cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let q_75 = match col.quantile_as_series(0.75) {
|
||||
Ok(ca) => match ca.cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let max = match col.max_as_series().cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let name = format!("{} ({})", col.name(), col.dtype());
|
||||
ChunkedArray::<Float64Type>::new_from_opt_slice(
|
||||
&name,
|
||||
&[
|
||||
Some(count),
|
||||
sum,
|
||||
mean,
|
||||
median,
|
||||
std,
|
||||
min,
|
||||
q_25,
|
||||
q_50,
|
||||
q_75,
|
||||
max,
|
||||
],
|
||||
)
|
||||
.into_series()
|
||||
});
|
||||
|
||||
let res = head.chain(tail).collect::<Vec<Series>>();
|
||||
let df = PolarsDF::new(res).map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
let df = NuDataFrame::dataframe_to_value(df, tag);
|
||||
Ok(OutputStream::one(df))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe drop"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Creates a new dataframe by dropping the selected columns"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe drop").rest(
|
||||
"rest",
|
||||
SyntaxShape::Any,
|
||||
"column names to be dropped",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "drop column a",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe drop a",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let columns: Vec<Value> = args.rest(0)?;
|
||||
let (col_string, col_span) = convert_columns(&columns, &tag)?;
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let new_df = match col_string.get(0) {
|
||||
Some(col) => df
|
||||
.as_ref()
|
||||
.drop(col)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None)),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Empty names list",
|
||||
"No column names where found",
|
||||
&col_span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
// If there are more columns in the drop selection list, these
|
||||
// are added from the resulting dataframe
|
||||
let res = col_string.iter().skip(1).try_fold(new_df, |new_df, col| {
|
||||
new_df
|
||||
.drop(col)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))
|
||||
})?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe drop-duplicates"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Drops duplicate values in dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe drop-duplicates")
|
||||
.optional(
|
||||
"subset",
|
||||
SyntaxShape::Table,
|
||||
"subset of columns to drop duplicates",
|
||||
)
|
||||
.switch("maintain", "maintain order", Some('m'))
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "drop duplicates",
|
||||
example: "[[a b]; [1 2] [3 4] [1 2]] | dataframe to-df | dataframe drop-duplicates",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
// Extracting the selection columns of the columns to perform the aggregation
|
||||
let columns: Option<Vec<Value>> = args.opt(0)?;
|
||||
let (subset, col_span) = match columns {
|
||||
Some(cols) => {
|
||||
let (agg_string, col_span) = convert_columns(&cols, &tag)?;
|
||||
(Some(agg_string), col_span)
|
||||
}
|
||||
None => (None, Span::unknown()),
|
||||
};
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let subset_slice = subset.as_ref().map(|cols| &cols[..]);
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.drop_duplicates(args.has_flag("maintain"), subset_slice)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe drop-nulls"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame, Series] Drops null values in dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe drop-nulls").optional(
|
||||
"subset",
|
||||
SyntaxShape::Table,
|
||||
"subset of columns to drop duplicates",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "drop null values in dataframe",
|
||||
example: r#"let df = ([[a b]; [1 2] [3 0] [1 2]] | dataframe to-df);
|
||||
let res = ($df.b / $df.b);
|
||||
let df = ($df | dataframe with-column $res --name res);
|
||||
$df | dataframe drop-nulls"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(1).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(2).into()],
|
||||
),
|
||||
Column::new(
|
||||
"res".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(1).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "drop null values in dataframe",
|
||||
example: r#"let s = ([1 2 0 0 3 4] | dataframe to-df);
|
||||
($s / $s) | dataframe drop-nulls"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"div_0_0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let value = args.input.next().ok_or_else(|| {
|
||||
ShellError::labeled_error("Empty stream", "No value found in stream", &tag.span)
|
||||
})?;
|
||||
|
||||
match value.value {
|
||||
UntaggedValue::DataFrame(df) => {
|
||||
// Extracting the selection columns of the columns to perform the aggregation
|
||||
let columns: Option<Vec<Value>> = args.opt(0)?;
|
||||
let (subset, col_span) = match columns {
|
||||
Some(cols) => {
|
||||
let (agg_string, col_span) = convert_columns(&cols, &tag)?;
|
||||
(Some(agg_string), col_span)
|
||||
}
|
||||
None => (None, Span::unknown()),
|
||||
};
|
||||
|
||||
let subset_slice = subset.as_ref().map(|cols| &cols[..]);
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.drop_nulls(subset_slice)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"drop nulls cannot be done with this value",
|
||||
&value.tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe dtypes"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Show dataframe data types"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe dtypes")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "drop column a",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe dtypes",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"column".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("b").into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"dtype".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("i64").into(),
|
||||
UntaggedValue::string("i64").into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_collect)]
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut dtypes: Vec<Value> = Vec::new();
|
||||
let names: Vec<Value> = df
|
||||
.as_ref()
|
||||
.get_column_names()
|
||||
.iter()
|
||||
.map(|v| {
|
||||
let dtype = df
|
||||
.as_ref()
|
||||
.column(v)
|
||||
.expect("using name from list of names from dataframe")
|
||||
.dtype();
|
||||
|
||||
let dtype_str = dtype.to_string();
|
||||
dtypes.push(Value {
|
||||
value: dtype_str.into(),
|
||||
tag: Tag::default(),
|
||||
});
|
||||
|
||||
Value {
|
||||
value: v.to_string().into(),
|
||||
tag: Tag::default(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let names_col = Column::new("column".to_string(), names);
|
||||
let dtypes_col = Column::new("dtype".to_string(), dtypes);
|
||||
|
||||
let df = NuDataFrame::try_from_columns(vec![names_col, dtypes_col], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use super::utils::parse_polars_error;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe to-dummies"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Creates a new dataframe with dummy variables"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe to-dummies")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Create new dataframe with dummy variables from a dataframe",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe to-dummies",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a_1".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(0).into()],
|
||||
),
|
||||
Column::new(
|
||||
"a_3".to_string(),
|
||||
vec![UntaggedValue::int(0).into(), UntaggedValue::int(1).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b_2".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(0).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b_4".to_string(),
|
||||
vec![UntaggedValue::int(0).into(), UntaggedValue::int(1).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Create new dataframe with dummy variables from a series",
|
||||
example: "[1 2 2 3 3] | dataframe to-df | dataframe to-dummies",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"0_1".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"0_2".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"0_3".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let value = args.input.next().ok_or_else(|| {
|
||||
ShellError::labeled_error("Empty stream", "No value found in stream", &tag.span)
|
||||
})?;
|
||||
|
||||
match value.value {
|
||||
UntaggedValue::DataFrame(df) => {
|
||||
let res = df.as_ref().to_dummies().map_err(|e| {
|
||||
parse_polars_error(
|
||||
&e,
|
||||
&tag.span,
|
||||
Some("The only allowed column types for dummies are String or Int"),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"dummies cannot be done with this value",
|
||||
&value.tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::parse_polars_error;
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe filter-with"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Filters dataframe using a mask as reference"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe filter-with").required(
|
||||
"mask",
|
||||
SyntaxShape::Any,
|
||||
"boolean mask used to filter data",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Filter dataframe using a bool mask",
|
||||
example: r#"let mask = ([$true $false] | dataframe to-df);
|
||||
[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe filter-with $mask"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::int(1).into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(2).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Filter dataframe by creating a mask from operation",
|
||||
example: r#"let mask = (([5 6] | dataframe to-df) > 5);
|
||||
[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe filter-with $mask"#,
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let value: Value = args.req(0)?;
|
||||
|
||||
let series_span = value.tag.span;
|
||||
let df = match value.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only add a series to a dataframe",
|
||||
value.tag.span,
|
||||
)),
|
||||
}?;
|
||||
let series = df.as_series(&series_span)?;
|
||||
let casted = series.bool().map_err(|e| {
|
||||
parse_polars_error(
|
||||
&e,
|
||||
&series_span,
|
||||
Some("Perhaps you want to use a series with booleans as mask"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.filter(casted)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe first"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Creates new dataframe with first rows"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe select").optional(
|
||||
"rows",
|
||||
SyntaxShape::Number,
|
||||
"Number of rows for head",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create new dataframe with head rows",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe first 1",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::int(1).into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(2).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let rows: Option<Tagged<usize>> = args.opt(0)?;
|
||||
|
||||
let rows = match rows {
|
||||
Some(val) => val.item,
|
||||
None => 5,
|
||||
};
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let res = df.as_ref().head(Some(rows));
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Creates dataframe with the selected columns"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get").rest(
|
||||
"rest",
|
||||
SyntaxShape::Any,
|
||||
"column names to sort dataframe",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Creates dataframe with selected columns",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe get a",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let columns: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let (col_string, col_span) = convert_columns(&columns, &tag)?;
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.select(&col_string)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{FrameStruct, NuDataFrame, NuGroupBy},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe group-by"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Creates a groupby object that can be used for other aggregations"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe group-by").rest("rest", SyntaxShape::Any, "groupby columns")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Grouping by column a",
|
||||
example: "[[a b]; [one 1] [one 2]] | dataframe to-df | dataframe group-by a",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
// Extracting the names of the columns to perform the groupby
|
||||
let by_columns: Vec<Value> = args.rest(0)?;
|
||||
let (columns_string, col_span) = convert_columns(&by_columns, &tag)?;
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
// This is the expensive part of the groupby; to create the
|
||||
// groups that will be used for grouping the data in the
|
||||
// dataframe. Once it has been done these values can be stored
|
||||
// in a NuGroupBy
|
||||
let groupby = df
|
||||
.as_ref()
|
||||
.groupby(&columns_string)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
|
||||
|
||||
let groups = groupby.get_groups().to_vec();
|
||||
let groupby = Value {
|
||||
tag,
|
||||
value: UntaggedValue::FrameStruct(FrameStruct::GroupBy(NuGroupBy::new(
|
||||
NuDataFrame::new(df.as_ref().clone()),
|
||||
columns_string,
|
||||
groups,
|
||||
))),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(groupby))
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
|
||||
use polars::prelude::JoinType;
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe join"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Joins a dataframe using columns as reference"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe join")
|
||||
.required("dataframe", SyntaxShape::Any, "right dataframe to join")
|
||||
.required_named(
|
||||
"left",
|
||||
SyntaxShape::Table,
|
||||
"left column names to perform join",
|
||||
Some('l'),
|
||||
)
|
||||
.required_named(
|
||||
"right",
|
||||
SyntaxShape::Table,
|
||||
"right column names to perform join",
|
||||
Some('r'),
|
||||
)
|
||||
.named(
|
||||
"type",
|
||||
SyntaxShape::String,
|
||||
"type of join. Inner by default",
|
||||
Some('t'),
|
||||
)
|
||||
.named(
|
||||
"suffix",
|
||||
SyntaxShape::String,
|
||||
"suffix for the columns of the right dataframe",
|
||||
Some('s'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "inner join dataframe",
|
||||
example: r#"let right = ([[a b c]; [1 2 5] [3 4 5] [5 6 6]] | dataframe to-df);
|
||||
$right | dataframe join $right -l [a b] -r [a b]"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"c".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"c_right".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let r_df: Value = args.req(0)?;
|
||||
let l_col: Vec<Value> = args.req_named("left")?;
|
||||
let r_col: Vec<Value> = args.req_named("right")?;
|
||||
let r_suffix: Option<Tagged<String>> = args.get_flag("suffix")?;
|
||||
let join_type_op: Option<Tagged<String>> = args.get_flag("type")?;
|
||||
|
||||
let join_type = match join_type_op {
|
||||
None => JoinType::Inner,
|
||||
Some(val) => match val.item.as_ref() {
|
||||
"inner" => JoinType::Inner,
|
||||
"outer" => JoinType::Outer,
|
||||
"left" => JoinType::Left,
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
"Incorrect join type",
|
||||
"Invalid join type",
|
||||
&val.tag,
|
||||
"Perhaps you mean: inner, outer or left",
|
||||
&val.tag,
|
||||
))
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let suffix = r_suffix.map(|s| s.item);
|
||||
|
||||
let (l_col_string, l_col_span) = convert_columns(&l_col, &tag)?;
|
||||
let (r_col_string, r_col_span) = convert_columns(&r_col, &tag)?;
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = match r_df.value {
|
||||
UntaggedValue::DataFrame(r_df) => {
|
||||
// Checking the column types before performing the join
|
||||
check_column_datatypes(
|
||||
df.as_ref(),
|
||||
r_df.as_ref(),
|
||||
&l_col_string,
|
||||
&l_col_span,
|
||||
&r_col_string,
|
||||
&r_col_span,
|
||||
)?;
|
||||
|
||||
df.as_ref()
|
||||
.join(
|
||||
r_df.as_ref(),
|
||||
&l_col_string,
|
||||
&r_col_string,
|
||||
join_type,
|
||||
suffix,
|
||||
)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &l_col_span, None))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Not a dataframe",
|
||||
"not a dataframe type value",
|
||||
&r_df.tag,
|
||||
)),
|
||||
}?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
fn check_column_datatypes<T: AsRef<str>>(
|
||||
df_l: &polars::prelude::DataFrame,
|
||||
df_r: &polars::prelude::DataFrame,
|
||||
l_cols: &[T],
|
||||
l_col_span: &Span,
|
||||
r_cols: &[T],
|
||||
r_col_span: &Span,
|
||||
) -> Result<(), ShellError> {
|
||||
if l_cols.len() != r_cols.len() {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
"Mismatched number of column names",
|
||||
format!(
|
||||
"found {} left names vs {} right names",
|
||||
l_cols.len(),
|
||||
r_cols.len()
|
||||
),
|
||||
l_col_span,
|
||||
"perhaps you need to change the number of columns to join",
|
||||
r_col_span,
|
||||
));
|
||||
}
|
||||
|
||||
for (l, r) in l_cols.iter().zip(r_cols) {
|
||||
let l_series = df_l
|
||||
.column(l.as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, l_col_span, None))?;
|
||||
|
||||
let r_series = df_r
|
||||
.column(r.as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, r_col_span, None))?;
|
||||
|
||||
if l_series.dtype() != r_series.dtype() {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
"Mismatched datatypes",
|
||||
format!(
|
||||
"left column type '{}' doesn't match '{}' right column match",
|
||||
l_series.dtype(),
|
||||
r_series.dtype()
|
||||
),
|
||||
l_col_span,
|
||||
"perhaps you need to select other column to match",
|
||||
r_col_span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe last"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Creates new dataframe with tail rows"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe last").optional(
|
||||
"n_rows",
|
||||
SyntaxShape::Number,
|
||||
"Number of rows for tail",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create new dataframe with last rows",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe last 1",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::int(3).into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(4).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let rows: Option<Tagged<usize>> = args.opt(0)?;
|
||||
|
||||
let rows = match rows {
|
||||
Some(val) => val.item,
|
||||
None => 5,
|
||||
};
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df.as_ref().tail(Some(rows));
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe list"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Lists stored dataframes"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe list")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let data = args
|
||||
.context
|
||||
.scope
|
||||
.get_vars()
|
||||
.into_iter()
|
||||
.filter_map(|(name, value)| {
|
||||
if let UntaggedValue::DataFrame(df) = &value.value {
|
||||
let rows = Value {
|
||||
value: (df.as_ref().height() as i64).into(),
|
||||
tag: Tag::default(),
|
||||
};
|
||||
|
||||
let cols = Value {
|
||||
value: (df.as_ref().width() as i64).into(),
|
||||
tag: Tag::default(),
|
||||
};
|
||||
|
||||
let location = match value.tag.anchor {
|
||||
Some(AnchorLocation::File(name)) => name,
|
||||
Some(AnchorLocation::Url(name)) => name,
|
||||
Some(AnchorLocation::Source(text)) => text.slice(0..text.end).text,
|
||||
None => "stream".to_string(),
|
||||
};
|
||||
|
||||
let location = Value {
|
||||
value: location.into(),
|
||||
tag: Tag::default(),
|
||||
};
|
||||
|
||||
let name = Value {
|
||||
value: name.into(),
|
||||
tag: Tag::default(),
|
||||
};
|
||||
|
||||
Some((name, rows, cols, location))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let mut name = Column::new_empty("name".to_string());
|
||||
let mut rows = Column::new_empty("rows".to_string());
|
||||
let mut cols = Column::new_empty("columns".to_string());
|
||||
let mut location = Column::new_empty("location".to_string());
|
||||
|
||||
for tuple in data {
|
||||
name.push(tuple.0);
|
||||
rows.push(tuple.1);
|
||||
cols.push(tuple.2);
|
||||
location.push(tuple.3);
|
||||
}
|
||||
|
||||
let tag = args.call_info.name_tag;
|
||||
let df = NuDataFrame::try_from_columns(vec![name, rows, cols, location], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Lists loaded dataframes in current scope",
|
||||
example: "let a = ([[a b];[1 2] [3 4]] | dataframe to-df); dataframe list",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("name".to_string(), vec![UntaggedValue::string("$a").into()]),
|
||||
Column::new("rows".to_string(), vec![UntaggedValue::int(2).into()]),
|
||||
Column::new("columns".to_string(), vec![UntaggedValue::int(2).into()]),
|
||||
Column::new(
|
||||
"location".to_string(),
|
||||
vec![UntaggedValue::string("stream").into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe melt"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Unpivot a DataFrame from wide to long format"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe melt")
|
||||
.required_named(
|
||||
"columns",
|
||||
SyntaxShape::Table,
|
||||
"column names for melting",
|
||||
Some('c'),
|
||||
)
|
||||
.required_named(
|
||||
"values",
|
||||
SyntaxShape::Table,
|
||||
"column names used as value columns",
|
||||
Some('v'),
|
||||
)
|
||||
.named(
|
||||
"variable_name",
|
||||
SyntaxShape::String,
|
||||
"optional name for variable column",
|
||||
Some('r'),
|
||||
)
|
||||
.named(
|
||||
"value_name",
|
||||
SyntaxShape::String,
|
||||
"optional name for value column",
|
||||
Some('l'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "melt dataframe",
|
||||
example:
|
||||
"[[a b c d]; [x 1 4 a] [y 2 5 b] [z 3 6 c]] | dataframe to-df | dataframe melt -c [b c] -v [a d]",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"c".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"variable".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("d").into(),
|
||||
UntaggedValue::string("d").into(),
|
||||
UntaggedValue::string("d").into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"value".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("x").into(),
|
||||
UntaggedValue::string("y").into(),
|
||||
UntaggedValue::string("z").into(),
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("b").into(),
|
||||
UntaggedValue::string("c").into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let id_col: Vec<Value> = args.req_named("columns")?;
|
||||
let val_col: Vec<Value> = args.req_named("values")?;
|
||||
|
||||
let value_name: Option<Tagged<String>> = args.get_flag("value_name")?;
|
||||
let variable_name: Option<Tagged<String>> = args.get_flag("variable_name")?;
|
||||
|
||||
let (id_col_string, id_col_span) = convert_columns(&id_col, &tag)?;
|
||||
let (val_col_string, val_col_span) = convert_columns(&val_col, &tag)?;
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
check_column_datatypes(df.as_ref(), &id_col_string, &id_col_span)?;
|
||||
check_column_datatypes(df.as_ref(), &val_col_string, &val_col_span)?;
|
||||
|
||||
let mut res = df
|
||||
.as_ref()
|
||||
.melt(&id_col_string, &val_col_string)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
if let Some(name) = &variable_name {
|
||||
res.rename("variable", &name.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &name.tag.span, None))?;
|
||||
}
|
||||
|
||||
if let Some(name) = &value_name {
|
||||
res.rename("value", &name.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &name.tag.span, None))?;
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
fn check_column_datatypes<T: AsRef<str>>(
|
||||
df: &polars::prelude::DataFrame,
|
||||
cols: &[T],
|
||||
col_span: &Span,
|
||||
) -> Result<(), ShellError> {
|
||||
if cols.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Merge error",
|
||||
"empty column list",
|
||||
col_span,
|
||||
));
|
||||
}
|
||||
|
||||
// Checking if they are same type
|
||||
if cols.len() > 1 {
|
||||
for w in cols.windows(2) {
|
||||
let l_series = df
|
||||
.column(w[0].as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, col_span, None))?;
|
||||
|
||||
let r_series = df
|
||||
.column(w[1].as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, col_span, None))?;
|
||||
|
||||
if l_series.dtype() != r_series.dtype() {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
"Merge error",
|
||||
"found different column types in list",
|
||||
col_span,
|
||||
format!(
|
||||
"datatypes {} and {} are incompatible",
|
||||
l_series.dtype(),
|
||||
r_series.dtype()
|
||||
),
|
||||
col_span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
pub mod aggregate;
|
||||
pub mod append;
|
||||
pub mod column;
|
||||
pub mod command;
|
||||
pub mod describe;
|
||||
pub mod drop;
|
||||
pub mod drop_duplicates;
|
||||
pub mod drop_nulls;
|
||||
pub mod dtypes;
|
||||
pub mod dummies;
|
||||
pub mod filter;
|
||||
pub mod first;
|
||||
pub mod get;
|
||||
pub mod groupby;
|
||||
pub mod join;
|
||||
pub mod last;
|
||||
pub mod list;
|
||||
pub mod melt;
|
||||
pub mod open;
|
||||
pub mod pivot;
|
||||
pub mod rename;
|
||||
pub mod sample;
|
||||
pub mod select;
|
||||
pub mod shape;
|
||||
pub mod show;
|
||||
pub mod slice;
|
||||
pub mod sort;
|
||||
pub mod take;
|
||||
pub mod to_csv;
|
||||
pub mod to_df;
|
||||
pub mod to_parquet;
|
||||
pub(crate) mod utils;
|
||||
pub mod where_;
|
||||
pub mod with_column;
|
||||
|
||||
pub use aggregate::DataFrame as DataFrameAggregate;
|
||||
pub use append::DataFrame as DataFrameAppend;
|
||||
pub use column::DataFrame as DataFrameColumn;
|
||||
pub use command::Command as DataFrame;
|
||||
pub use describe::DataFrame as DataFrameDescribe;
|
||||
pub use drop::DataFrame as DataFrameDrop;
|
||||
pub use drop_duplicates::DataFrame as DataFrameDropDuplicates;
|
||||
pub use drop_nulls::DataFrame as DataFrameDropNulls;
|
||||
pub use dtypes::DataFrame as DataFrameDTypes;
|
||||
pub use dummies::DataFrame as DataFrameDummies;
|
||||
pub use filter::DataFrame as DataFrameFilter;
|
||||
pub use first::DataFrame as DataFrameFirst;
|
||||
pub use get::DataFrame as DataFrameGet;
|
||||
pub use groupby::DataFrame as DataFrameGroupBy;
|
||||
pub use join::DataFrame as DataFrameJoin;
|
||||
pub use last::DataFrame as DataFrameLast;
|
||||
pub use list::DataFrame as DataFrameList;
|
||||
pub use melt::DataFrame as DataFrameMelt;
|
||||
pub use open::DataFrame as DataFrameOpen;
|
||||
pub use pivot::DataFrame as DataFramePivot;
|
||||
pub use rename::DataFrame as DataFrameRename;
|
||||
pub use sample::DataFrame as DataFrameSample;
|
||||
pub use select::DataFrame as DataFrameSelect;
|
||||
pub use shape::DataFrame as DataFrameShape;
|
||||
pub use show::DataFrame as DataFrameShow;
|
||||
pub use slice::DataFrame as DataFrameSlice;
|
||||
pub use sort::DataFrame as DataFrameSort;
|
||||
pub use take::DataFrame as DataFrameTake;
|
||||
pub use to_csv::DataFrame as DataFrameToCsv;
|
||||
pub use to_df::DataFrame as DataFrameToDF;
|
||||
pub use to_parquet::DataFrame as DataFrameToParquet;
|
||||
pub use where_::DataFrame as DataFrameWhere;
|
||||
pub use with_column::DataFrame as DataFrameWithColumn;
|
||||
|
||||
pub mod series;
|
||||
pub use series::DataFrameAllFalse;
|
||||
pub use series::DataFrameAllTrue;
|
||||
pub use series::DataFrameArgMax;
|
||||
pub use series::DataFrameArgMin;
|
||||
pub use series::DataFrameArgSort;
|
||||
pub use series::DataFrameArgTrue;
|
||||
pub use series::DataFrameArgUnique;
|
||||
pub use series::DataFrameConcatenate;
|
||||
pub use series::DataFrameContains;
|
||||
pub use series::DataFrameCumulative;
|
||||
pub use series::DataFrameGetDay;
|
||||
pub use series::DataFrameGetHour;
|
||||
pub use series::DataFrameGetMinute;
|
||||
pub use series::DataFrameGetMonth;
|
||||
pub use series::DataFrameGetNanoSecond;
|
||||
pub use series::DataFrameGetOrdinal;
|
||||
pub use series::DataFrameGetSecond;
|
||||
pub use series::DataFrameGetWeek;
|
||||
pub use series::DataFrameGetWeekDay;
|
||||
pub use series::DataFrameGetYear;
|
||||
pub use series::DataFrameIsDuplicated;
|
||||
pub use series::DataFrameIsIn;
|
||||
pub use series::DataFrameIsNotNull;
|
||||
pub use series::DataFrameIsNull;
|
||||
pub use series::DataFrameIsUnique;
|
||||
pub use series::DataFrameNNull;
|
||||
pub use series::DataFrameNUnique;
|
||||
pub use series::DataFrameNot;
|
||||
pub use series::DataFrameReplace;
|
||||
pub use series::DataFrameReplaceAll;
|
||||
pub use series::DataFrameRolling;
|
||||
pub use series::DataFrameSeriesRename;
|
||||
pub use series::DataFrameSet;
|
||||
pub use series::DataFrameSetWithIdx;
|
||||
pub use series::DataFrameShift;
|
||||
pub use series::DataFrameStrFTime;
|
||||
pub use series::DataFrameStringLengths;
|
||||
pub use series::DataFrameStringSlice;
|
||||
pub use series::DataFrameToLowercase;
|
||||
pub use series::DataFrameToUppercase;
|
||||
pub use series::DataFrameUnique;
|
||||
pub use series::DataFrameValueCounts;
|
@ -1,211 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::NuDataFrame, Primitive, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::{CsvEncoding, CsvReader, JsonReader, ParquetReader, SerReader};
|
||||
use std::fs::File;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe open"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Opens csv, json or parquet file to create dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe open")
|
||||
.required(
|
||||
"file",
|
||||
SyntaxShape::FilePath,
|
||||
"file path to load values from",
|
||||
)
|
||||
.named(
|
||||
"delimiter",
|
||||
SyntaxShape::String,
|
||||
"file delimiter character. CSV file",
|
||||
Some('d'),
|
||||
)
|
||||
.switch(
|
||||
"no_header",
|
||||
"Indicates if file doesn't have header. CSV file",
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"infer_schema",
|
||||
SyntaxShape::Number,
|
||||
"Set number of rows to infer the schema of the file. CSV file",
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"skip_rows",
|
||||
SyntaxShape::Number,
|
||||
"Number of rows to skip from file. CSV file",
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table,
|
||||
"Columns to be selected from csv file. CSV file",
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Takes a file name and creates a dataframe",
|
||||
example: "dataframe open test.csv",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let file: Tagged<PathBuf> = args.req(0)?;
|
||||
|
||||
let df = match file.item().extension() {
|
||||
Some(e) => match e.to_str() {
|
||||
Some("csv") => from_csv(args),
|
||||
Some("parquet") => from_parquet(args),
|
||||
Some("json") => from_json(args),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Error with file",
|
||||
"Not a csv, parquet or json file",
|
||||
&file.tag,
|
||||
)),
|
||||
},
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Error with file",
|
||||
"File without extension",
|
||||
&file.tag,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let file_name = match file.item.into_os_string().into_string() {
|
||||
Ok(name) => name,
|
||||
Err(e) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"File Name Error",
|
||||
format!("{:?}", e),
|
||||
&file.tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let df_tag = Tag {
|
||||
anchor: Some(AnchorLocation::File(file_name)),
|
||||
span: tag.span,
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(
|
||||
df, df_tag,
|
||||
)))
|
||||
}
|
||||
|
||||
fn from_parquet(args: CommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
let file: Tagged<PathBuf> = args.req(0)?;
|
||||
|
||||
let r = File::open(&file.item)
|
||||
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))?;
|
||||
|
||||
let reader = ParquetReader::new(r);
|
||||
|
||||
reader
|
||||
.finish()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &file.tag.span, None))
|
||||
}
|
||||
|
||||
fn from_json(args: CommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
let file: Tagged<PathBuf> = args.req(0)?;
|
||||
|
||||
let r = File::open(&file.item)
|
||||
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))?;
|
||||
|
||||
let reader = JsonReader::new(r);
|
||||
|
||||
reader
|
||||
.finish()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &file.tag.span, None))
|
||||
}
|
||||
|
||||
fn from_csv(args: CommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
let file: Tagged<PathBuf> = args.req(0)?;
|
||||
let delimiter: Option<Tagged<String>> = args.get_flag("delimiter")?;
|
||||
let no_header: bool = args.has_flag("no_header");
|
||||
let infer_schema: Option<Tagged<usize>> = args.get_flag("infer_schema")?;
|
||||
let skip_rows: Option<Tagged<usize>> = args.get_flag("skip_rows")?;
|
||||
let columns: Option<Vec<Value>> = args.get_flag("columns")?;
|
||||
|
||||
let csv_reader = CsvReader::from_path(&file.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &file.tag.span, None))?
|
||||
.with_encoding(CsvEncoding::LossyUtf8);
|
||||
|
||||
let csv_reader = match delimiter {
|
||||
None => csv_reader,
|
||||
Some(d) => {
|
||||
if d.item.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Incorrect delimiter",
|
||||
"Delimiter has to be one char",
|
||||
&d.tag,
|
||||
));
|
||||
} else {
|
||||
let delimiter = match d.item.chars().next() {
|
||||
Some(d) => d as u8,
|
||||
None => unreachable!(),
|
||||
};
|
||||
csv_reader.with_delimiter(delimiter)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let csv_reader = csv_reader.has_header(!no_header);
|
||||
|
||||
let csv_reader = match infer_schema {
|
||||
None => csv_reader,
|
||||
Some(r) => csv_reader.infer_schema(Some(r.item)),
|
||||
};
|
||||
|
||||
let csv_reader = match skip_rows {
|
||||
None => csv_reader,
|
||||
Some(r) => csv_reader.with_skip_rows(r.item),
|
||||
};
|
||||
|
||||
let csv_reader = match columns {
|
||||
None => csv_reader,
|
||||
Some(c) => {
|
||||
let columns = c
|
||||
.into_iter()
|
||||
.map(|value| match value.value {
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => Ok(s),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type for column",
|
||||
"Only string as columns",
|
||||
&value.tag,
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<String>, ShellError>>();
|
||||
|
||||
csv_reader.with_columns(Some(columns?))
|
||||
}
|
||||
};
|
||||
|
||||
match csv_reader.finish() {
|
||||
Ok(df) => Ok(df),
|
||||
Err(e) => Err(parse_polars_error::<&str>(&e, &file.tag.span, None)),
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
use super::utils::parse_polars_error;
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe rename-col"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] rename a dataframe column"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe rename-col")
|
||||
.required("from", SyntaxShape::String, "column name to be renamed")
|
||||
.required("to", SyntaxShape::String, "new column name")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Renames a dataframe column",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe rename-col a ab",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"ab".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let from: Tagged<String> = args.req(0)?;
|
||||
let to: Tagged<String> = args.req(1)?;
|
||||
|
||||
let (mut df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
df.as_mut()
|
||||
.rename(&from.item, &to.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe sample"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Create sample dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe sample")
|
||||
.named(
|
||||
"n_rows",
|
||||
SyntaxShape::Number,
|
||||
"number of rows to be taken from dataframe",
|
||||
Some('n'),
|
||||
)
|
||||
.named(
|
||||
"fraction",
|
||||
SyntaxShape::Number,
|
||||
"fraction of dataframe to be taken",
|
||||
Some('f'),
|
||||
)
|
||||
.switch("replace", "sample with replace", Some('e'))
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Sample rows from dataframe",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe sample -n 1",
|
||||
result: None, // No expected value because sampling is random
|
||||
},
|
||||
Example {
|
||||
description: "Shows sample row using fraction and replace",
|
||||
example:
|
||||
"[[a b]; [1 2] [3 4] [5 6]] | dataframe to-df | dataframe sample -f 0.5 -e",
|
||||
result: None, // No expected value because sampling is random
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let rows: Option<Tagged<usize>> = args.get_flag("n_rows")?;
|
||||
let fraction: Option<Tagged<f64>> = args.get_flag("fraction")?;
|
||||
let replace: bool = args.has_flag("replace");
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = match (rows, fraction) {
|
||||
(Some(rows), None) => df
|
||||
.as_ref()
|
||||
.sample_n(rows.item, replace)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &rows.tag.span, None)),
|
||||
(None, Some(frac)) => df
|
||||
.as_ref()
|
||||
.sample_frac(frac.item, replace)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &frac.tag.span, None)),
|
||||
(Some(_), Some(_)) => Err(ShellError::labeled_error(
|
||||
"Incompatible flags",
|
||||
"Only one selection criterion allowed",
|
||||
&tag,
|
||||
)),
|
||||
(None, None) => Err(ShellError::labeled_error_with_secondary(
|
||||
"No selection",
|
||||
"No selection criterion was found",
|
||||
&tag,
|
||||
"Perhaps you want to use the flag -n or -f",
|
||||
&tag,
|
||||
)),
|
||||
}?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe select"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Creates a new dataframe with the selected columns"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe select").rest("rest", SyntaxShape::Any, "selected column names")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create new dataframe with column a",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe select a",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let columns: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let (col_string, col_span) = convert_columns(&columns, &tag)?;
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.select(&col_string)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe all-false"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Returns true if all values are false"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe all-false")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Returns true if all values are false",
|
||||
example: "[$false $false $false] | dataframe to-df | dataframe all-false",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"all_false".to_string(),
|
||||
vec![UntaggedValue::boolean(true).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Checks the result from a comparison",
|
||||
example: r#"let s = ([5 6 2 10] | dataframe to-df);
|
||||
let res = ($s > 9);
|
||||
$res | dataframe all-false"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"all_false".to_string(),
|
||||
vec![UntaggedValue::boolean(false).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let bool = series.bool().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&tag.span,
|
||||
Some("all-false only works with series of type bool"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = bool.all_false();
|
||||
|
||||
let value = Value {
|
||||
value: UntaggedValue::Primitive(res.into()),
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
let df = NuDataFrame::try_from_columns(
|
||||
vec![Column::new("all_false".to_string(), vec![value])],
|
||||
&tag.span,
|
||||
)?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe all-true"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Returns true if all values are true"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe all-true")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Returns true if all values are true",
|
||||
example: "[$true $true $true] | dataframe to-df | dataframe all-true",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"all_true".to_string(),
|
||||
vec![UntaggedValue::boolean(true).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Checks the result from a comparison",
|
||||
example: r#"let s = ([5 6 2 8] | dataframe to-df);
|
||||
let res = ($s > 9);
|
||||
$res | dataframe all-true"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"all_true".to_string(),
|
||||
vec![UntaggedValue::boolean(false).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let bool = series.bool().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&tag.span,
|
||||
Some("all-true only works with series of type bool"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = bool.all_true();
|
||||
|
||||
let value = Value {
|
||||
value: UntaggedValue::Primitive(res.into()),
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
let df = NuDataFrame::try_from_columns(
|
||||
vec![Column::new("all_true".to_string(), vec![value])],
|
||||
&tag.span,
|
||||
)?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::{IntoSeries, NewChunkedArray, UInt32Chunked};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe arg-max"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Return index for max value in series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe arg-max")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns index for max value",
|
||||
example: "[1 3 2] | dataframe to-df | dataframe arg-max",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_max".to_string(),
|
||||
vec![UntaggedValue::int(1).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let res = series.arg_max();
|
||||
|
||||
let chunked = match res {
|
||||
Some(index) => UInt32Chunked::new_from_slice("arg_max", &[index as u32]),
|
||||
None => UInt32Chunked::new_from_slice("arg_max", &[]),
|
||||
};
|
||||
|
||||
let res = chunked.into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::{IntoSeries, NewChunkedArray, UInt32Chunked};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe arg-min"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Return index for min value in series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe arg-min")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns index for min value",
|
||||
example: "[1 3 2] | dataframe to-df | dataframe arg-min",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_min".to_string(),
|
||||
vec![UntaggedValue::int(0).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df.as_series(&df_tag.span)?.arg_min();
|
||||
|
||||
let chunked = match res {
|
||||
Some(index) => UInt32Chunked::new_from_slice("arg_min", &[index as u32]),
|
||||
None => UInt32Chunked::new_from_slice("arg_min", &[]),
|
||||
};
|
||||
|
||||
let res = chunked.into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe arg-sort"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Returns indexes for a sorted series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe arg-sort").switch("reverse", "reverse order", Some('r'))
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns indexes for a sorted series",
|
||||
example: "[1 2 2 3 3] | dataframe to-df | dataframe arg-sort",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_sort".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let reverse = args.has_flag("reverse");
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut res = df.as_series(&df_tag.span)?.argsort(reverse).into_series();
|
||||
res.rename("arg_sort");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe arg-true"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Returns indexes where values are true"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe arg-true")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns indexes where values are true",
|
||||
example: "[$false $true $false] | dataframe to-df | dataframe arg-true",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_true".to_string(),
|
||||
vec![UntaggedValue::int(1).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let bool = series.bool().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&tag.span,
|
||||
Some("arg-true only works with series of type bool"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut res = bool.arg_true().into_series();
|
||||
res.rename("arg_true");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe arg-unique"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Returns indexes for unique values"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe arg-unique")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns indexes for unique values",
|
||||
example: "[1 2 2 3 3] | dataframe to-df | dataframe arg-unique",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_unique".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut res = df
|
||||
.as_series(&df_tag.span)?
|
||||
.arg_unique()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?
|
||||
.into_series();
|
||||
|
||||
res.rename("arg_unique");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe concatenate"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Concatenates strings with other array"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe concatenate").required(
|
||||
"other",
|
||||
SyntaxShape::Any,
|
||||
"Other array with string to be concatenated",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Concatenate string",
|
||||
example: r#"let other = ([za xs cd] | dataframe to-df);
|
||||
[abc abc abc] | dataframe to-df | dataframe concatenate $other"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("abcza").into(),
|
||||
UntaggedValue::string("abcxs").into(),
|
||||
UntaggedValue::string("abccd").into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let other: Value = args.req(0)?;
|
||||
|
||||
let other_df = match &other.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only concatenate another series",
|
||||
other.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let other_series = other_df.as_series(&other.tag.span)?;
|
||||
let other_chunked = other_series.utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&other.tag.span,
|
||||
Some("The concatenate command can only be used with string columns"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let chunked = series.utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&df_tag.span,
|
||||
Some("The concatenate command can only be used with string columns"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut res = chunked.concat(other_chunked);
|
||||
|
||||
res.rename(series.name());
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe contains"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Checks if a pattern is contained in a string"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe contains").required(
|
||||
"pattern",
|
||||
SyntaxShape::String,
|
||||
"Regex pattern to be searched",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns boolean indicating if pattern was found",
|
||||
example: "[abc acb acb] | dataframe to-df | dataframe contains ab",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let pattern: Tagged<String> = args.req(0)?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let chunked = series.utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&df_tag.span,
|
||||
Some("The contains command can only be used with string columns"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.contains(&pattern.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::DataType;
|
||||
|
||||
enum CumType {
|
||||
Min,
|
||||
Max,
|
||||
Sum,
|
||||
}
|
||||
|
||||
impl CumType {
|
||||
fn from_str(roll_type: &str, span: &Span) -> Result<Self, ShellError> {
|
||||
match roll_type {
|
||||
"min" => Ok(Self::Min),
|
||||
"max" => Ok(Self::Max),
|
||||
"sum" => Ok(Self::Sum),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Wrong operation",
|
||||
"Operation not valid for cumulative",
|
||||
span,
|
||||
"Perhaps you want to use: max, min, sum",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
CumType::Min => "cum_min",
|
||||
CumType::Max => "cum_max",
|
||||
CumType::Sum => "cum_sum",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe cum"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Cumulative calculation for a series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe cum")
|
||||
.required("type", SyntaxShape::String, "rolling operation")
|
||||
.switch("reverse", "Reverse cumulative calculation", Some('r'))
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Cumulative sum for a series",
|
||||
example: "[1 2 3 4 5] | dataframe to-df | dataframe cum sum",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0_cum_sum".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
UntaggedValue::int(10).into(),
|
||||
UntaggedValue::int(15).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let cum_type: Tagged<String> = args.req(0)?;
|
||||
let reverse = args.has_flag("reverse");
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
if let DataType::Object(_) = series.dtype() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Found object series",
|
||||
"Series of type object cannot be used for cumulative operation",
|
||||
&df_tag.span,
|
||||
));
|
||||
}
|
||||
|
||||
let cum_type = CumType::from_str(&cum_type.item, &cum_type.tag.span)?;
|
||||
let mut res = match cum_type {
|
||||
CumType::Max => series.cummax(reverse),
|
||||
CumType::Min => series.cummin(reverse),
|
||||
CumType::Sum => series.cumsum(reverse),
|
||||
};
|
||||
|
||||
let name = format!("{}_{}", series.name(), cum_type.to_str());
|
||||
res.rename(&name);
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-day"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets day from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-day")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns day from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-day"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(4).into(), UntaggedValue::int(4).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.day().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-hour"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets hour from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-hour")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns hour from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-hour"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(16).into(), UntaggedValue::int(16).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.hour().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-minute"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets minute from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-minute")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns minute from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-minute"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(39).into(), UntaggedValue::int(39).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.minute().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-month"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets month from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-month")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns month from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-month"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(8).into(), UntaggedValue::int(8).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.month().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-nanosecond"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets nanosecond from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-nanosecond")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns nanosecond from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-nanosecond"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(0).into(), UntaggedValue::int(0).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.nanosecond().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-ordinal"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets ordinal date from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-ordinal")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns ordinal from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-ordinal"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(217).into(),
|
||||
UntaggedValue::int(217).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.ordinal().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-second"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets second from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-second")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns second from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-second"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(18).into(), UntaggedValue::int(18).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.second().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-week"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets week from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-week")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns week from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-week"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(32).into(), UntaggedValue::int(32).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.week().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-weekday"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets weekday from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-weekday")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns weekday from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-weekday"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(1).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.weekday().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-year"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets year from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-year")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns year from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-year"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(2020).into(),
|
||||
UntaggedValue::int(2020).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.year().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe is-duplicated"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Creates mask indicating duplicated values"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe is-duplicated")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create mask indicating duplicated values",
|
||||
example: "[5 6 6 6 8 8 8] | dataframe to-df | dataframe is-duplicated",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_duplicated".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut res = df
|
||||
.as_series(&df_tag.span)?
|
||||
.is_duplicated()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?
|
||||
.into_series();
|
||||
|
||||
res.rename("is_duplicated");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe is-in"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Checks if elements from a series are contained in right series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe is-in").required("other", SyntaxShape::Any, "right series")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Checks if elements from a series are contained in right series",
|
||||
example: r#"let other = ([1 3 6] | dataframe to-df);
|
||||
[5 6 6 6 8 8 8] | dataframe to-df | dataframe is-in $other"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_in".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let value: Value = args.req(0)?;
|
||||
|
||||
let other_df = match value.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only search in a series",
|
||||
value.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let other = other_df.as_series(&value.tag.span)?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut res = df
|
||||
.as_series(&df_tag.span)?
|
||||
.is_in(&other)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?
|
||||
.into_series();
|
||||
|
||||
res.rename("is_in");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe is-not-null"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Creates mask where value is not null"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe is-not-null")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create mask where values are not null",
|
||||
example: r#"let s = ([5 6 0 8] | dataframe to-df);
|
||||
let res = ($s / $s);
|
||||
$res | dataframe is-not-null"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_not_null".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df.as_series(&df_tag.span)?.is_not_null();
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe is-null"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Creates mask where value is null"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe is-null")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create mask where values are null",
|
||||
example: r#"let s = ([5 6 0 8] | dataframe to-df);
|
||||
let res = ($s / $s);
|
||||
$res | dataframe is-null"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_null".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df.as_series(&df_tag.span)?.is_null();
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe is-unique"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Creates mask indicating unique values"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe is-unique")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create mask indicating unique values",
|
||||
example: "[5 6 6 6 8 8 8] | dataframe to-df | dataframe is-unique",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_unique".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut res = df
|
||||
.as_series(&df_tag.span)?
|
||||
.is_unique()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?
|
||||
.into_series();
|
||||
|
||||
res.rename("is_unique");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
pub mod all_false;
|
||||
pub mod all_true;
|
||||
pub mod arg_max;
|
||||
pub mod arg_min;
|
||||
pub mod arg_sort;
|
||||
pub mod arg_true;
|
||||
pub mod arg_unique;
|
||||
pub mod concatenate;
|
||||
pub mod contains;
|
||||
pub mod cumulative;
|
||||
pub mod get_day;
|
||||
pub mod get_hour;
|
||||
pub mod get_minute;
|
||||
pub mod get_month;
|
||||
pub mod get_nanosecond;
|
||||
pub mod get_ordinal;
|
||||
pub mod get_second;
|
||||
pub mod get_week;
|
||||
pub mod get_weekday;
|
||||
pub mod get_year;
|
||||
pub mod is_duplicated;
|
||||
pub mod is_in;
|
||||
pub mod is_not_null;
|
||||
pub mod is_null;
|
||||
pub mod is_unique;
|
||||
pub mod n_null;
|
||||
pub mod n_unique;
|
||||
pub mod not;
|
||||
pub mod rename;
|
||||
pub mod replace;
|
||||
pub mod replace_all;
|
||||
pub mod rolling;
|
||||
pub mod set;
|
||||
pub mod set_with_idx;
|
||||
pub mod shift;
|
||||
pub mod str_lengths;
|
||||
pub mod str_slice;
|
||||
pub mod strftime;
|
||||
pub mod to_lowercase;
|
||||
pub mod to_uppercase;
|
||||
pub mod unique;
|
||||
pub mod value_counts;
|
||||
|
||||
pub use all_false::DataFrame as DataFrameAllFalse;
|
||||
pub use all_true::DataFrame as DataFrameAllTrue;
|
||||
pub use arg_max::DataFrame as DataFrameArgMax;
|
||||
pub use arg_min::DataFrame as DataFrameArgMin;
|
||||
pub use arg_sort::DataFrame as DataFrameArgSort;
|
||||
pub use arg_true::DataFrame as DataFrameArgTrue;
|
||||
pub use arg_unique::DataFrame as DataFrameArgUnique;
|
||||
pub use concatenate::DataFrame as DataFrameConcatenate;
|
||||
pub use contains::DataFrame as DataFrameContains;
|
||||
pub use cumulative::DataFrame as DataFrameCumulative;
|
||||
pub use get_day::DataFrame as DataFrameGetDay;
|
||||
pub use get_hour::DataFrame as DataFrameGetHour;
|
||||
pub use get_minute::DataFrame as DataFrameGetMinute;
|
||||
pub use get_month::DataFrame as DataFrameGetMonth;
|
||||
pub use get_nanosecond::DataFrame as DataFrameGetNanoSecond;
|
||||
pub use get_ordinal::DataFrame as DataFrameGetOrdinal;
|
||||
pub use get_second::DataFrame as DataFrameGetSecond;
|
||||
pub use get_week::DataFrame as DataFrameGetWeek;
|
||||
pub use get_weekday::DataFrame as DataFrameGetWeekDay;
|
||||
pub use get_year::DataFrame as DataFrameGetYear;
|
||||
pub use is_duplicated::DataFrame as DataFrameIsDuplicated;
|
||||
pub use is_in::DataFrame as DataFrameIsIn;
|
||||
pub use is_not_null::DataFrame as DataFrameIsNotNull;
|
||||
pub use is_null::DataFrame as DataFrameIsNull;
|
||||
pub use is_unique::DataFrame as DataFrameIsUnique;
|
||||
pub use n_null::DataFrame as DataFrameNNull;
|
||||
pub use n_unique::DataFrame as DataFrameNUnique;
|
||||
pub use not::DataFrame as DataFrameNot;
|
||||
pub use rename::DataFrame as DataFrameSeriesRename;
|
||||
pub use replace::DataFrame as DataFrameReplace;
|
||||
pub use replace_all::DataFrame as DataFrameReplaceAll;
|
||||
pub use rolling::DataFrame as DataFrameRolling;
|
||||
pub use set::DataFrame as DataFrameSet;
|
||||
pub use set_with_idx::DataFrame as DataFrameSetWithIdx;
|
||||
pub use shift::DataFrame as DataFrameShift;
|
||||
pub use str_lengths::DataFrame as DataFrameStringLengths;
|
||||
pub use str_slice::DataFrame as DataFrameStringSlice;
|
||||
pub use strftime::DataFrame as DataFrameStrFTime;
|
||||
pub use to_lowercase::DataFrame as DataFrameToLowercase;
|
||||
pub use to_uppercase::DataFrame as DataFrameToUppercase;
|
||||
pub use unique::DataFrame as DataFrameUnique;
|
||||
pub use value_counts::DataFrame as DataFrameValueCounts;
|
@ -1,77 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Primitive, Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe count-null"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Counts null values"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe count-null")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Counts null values",
|
||||
example: r#"let s = ([1 1 0 0 3 3 4] | dataframe to-df);
|
||||
($s / $s) | dataframe count-null"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"count_null".to_string(),
|
||||
vec![UntaggedValue::int(2).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df.as_series(&df_tag.span)?.null_count();
|
||||
|
||||
let value = Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Int(res as i64)),
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
let df = NuDataFrame::try_from_columns(
|
||||
vec![Column::new("count_null".to_string(), vec![value])],
|
||||
&tag.span,
|
||||
)?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Primitive, Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe count-unique"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Counts unique value"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe count-unique")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Counts unique values",
|
||||
example: "[1 1 2 2 3 3 4] | dataframe to-df | dataframe count-unique",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"count_unique".to_string(),
|
||||
vec![UntaggedValue::int(4).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_series(&df_tag.span)?
|
||||
.n_unique()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
let value = Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Int(res as i64)),
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
let df = NuDataFrame::try_from_columns(
|
||||
vec![Column::new("count_unique".to_string(), vec![value])],
|
||||
&tag.span,
|
||||
)?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
use std::ops::Not;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe not"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Inverts boolean mask"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe not")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Inverts boolean mask",
|
||||
example: "[$true $false $true] | dataframe to-df | dataframe not",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let bool = series.bool().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&tag.span,
|
||||
Some("not only works with series of type bool"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = bool.not();
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe rename"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Renames a series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe rename").required(
|
||||
"name",
|
||||
SyntaxShape::String,
|
||||
"new series name",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Renames a series",
|
||||
example: "[5 6 7 8] | dataframe to-df | dataframe rename new_name",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"new_name".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
UntaggedValue::int(7).into(),
|
||||
UntaggedValue::int(8).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let name: Tagged<String> = args.req(0)?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut series = df.as_series(&df_tag.span)?;
|
||||
|
||||
series.rename(&name.item);
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![series], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::{Span, Tagged};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe replace"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Replace the leftmost (sub)string by a regex pattern"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe replace")
|
||||
.required_named(
|
||||
"pattern",
|
||||
SyntaxShape::String,
|
||||
"Regex pattern to be matched",
|
||||
Some('p'),
|
||||
)
|
||||
.required_named(
|
||||
"replace",
|
||||
SyntaxShape::String,
|
||||
"replacing string",
|
||||
Some('r'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Replaces string",
|
||||
example: "[abc abc abc] | dataframe to-df | dataframe replace -p ab -r AB",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("ABc").into(),
|
||||
UntaggedValue::string("ABc").into(),
|
||||
UntaggedValue::string("ABc").into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let pattern: Tagged<String> = args.req_named("pattern")?;
|
||||
let replace: Tagged<String> = args.req_named("replace")?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let chunked = series.utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&df_tag.span,
|
||||
Some("The replace-all command can only be used with string columns"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut res = chunked
|
||||
.replace(&pattern.item, &replace.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
res.rename(series.name());
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe replace-all"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Replace all (sub)strings by a regex pattern"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe replace")
|
||||
.required_named(
|
||||
"pattern",
|
||||
SyntaxShape::String,
|
||||
"Regex pattern to be matched",
|
||||
Some('p'),
|
||||
)
|
||||
.required_named(
|
||||
"replace",
|
||||
SyntaxShape::String,
|
||||
"replacing string",
|
||||
Some('r'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Replaces string",
|
||||
example: "[abac abac abac] | dataframe to-df | dataframe replace-all -p a -r A",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("AbAc").into(),
|
||||
UntaggedValue::string("AbAc").into(),
|
||||
UntaggedValue::string("AbAc").into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let pattern: Tagged<String> = args.req_named("pattern")?;
|
||||
let replace: Tagged<String> = args.req_named("replace")?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let chunked = series.utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&df_tag.span,
|
||||
Some("The replace command can only be used with string columns"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut res = chunked
|
||||
.replace_all(&pattern.item, &replace.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
res.rename(series.name());
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::{DataType, RollingOptions};
|
||||
|
||||
enum RollType {
|
||||
Min,
|
||||
Max,
|
||||
Sum,
|
||||
Mean,
|
||||
}
|
||||
|
||||
impl RollType {
|
||||
fn from_str(roll_type: &str, span: &Span) -> Result<Self, ShellError> {
|
||||
match roll_type {
|
||||
"min" => Ok(Self::Min),
|
||||
"max" => Ok(Self::Max),
|
||||
"sum" => Ok(Self::Sum),
|
||||
"mean" => Ok(Self::Mean),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Wrong operation",
|
||||
"Operation not valid for rolling",
|
||||
span,
|
||||
"Perhaps you want to use: max, min, sum, mean",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
RollType::Min => "rolling_min",
|
||||
RollType::Max => "rolling_max",
|
||||
RollType::Sum => "rolling_sum",
|
||||
RollType::Mean => "rolling_mean",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe rolling"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Rolling calculation for a series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe rolling")
|
||||
.required("type", SyntaxShape::String, "rolling operation")
|
||||
.required("window", SyntaxShape::Int, "Window size for rolling")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Rolling sum for a series",
|
||||
example:
|
||||
"[1 2 3 4 5] | dataframe to-df | dataframe rolling sum 2 | dataframe drop-nulls",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0_rolling_sum".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(7).into(),
|
||||
UntaggedValue::int(9).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Rolling max for a series",
|
||||
example:
|
||||
"[1 2 3 4 5] | dataframe to-df | dataframe rolling max 2 | dataframe drop-nulls",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0_rolling_max".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let roll_type: Tagged<String> = args.req(0)?;
|
||||
let window_size: Tagged<i64> = args.req(1)?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
if let DataType::Object(_) = series.dtype() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Found object series",
|
||||
"Series of type object cannot be used for rolling operation",
|
||||
&df_tag.span,
|
||||
));
|
||||
}
|
||||
|
||||
let roll_type = RollType::from_str(&roll_type.item, &roll_type.tag.span)?;
|
||||
let rolling_opts = RollingOptions {
|
||||
window_size: window_size.item as usize,
|
||||
min_periods: window_size.item as usize,
|
||||
weights: None,
|
||||
center: false,
|
||||
};
|
||||
let res = match roll_type {
|
||||
RollType::Max => series.rolling_max(rolling_opts),
|
||||
RollType::Min => series.rolling_min(rolling_opts),
|
||||
RollType::Sum => series.rolling_sum(rolling_opts),
|
||||
RollType::Mean => series.rolling_mean(rolling_opts),
|
||||
};
|
||||
|
||||
let mut res = res.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let name = format!("{}_{}", series.name(), roll_type.to_str());
|
||||
res.rename(&name);
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Primitive, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use polars::prelude::{ChunkSet, DataType, IntoSeries};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe set"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Sets value where given mask is true"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe set")
|
||||
.required("value", SyntaxShape::Any, "value to be inserted in series")
|
||||
.required_named(
|
||||
"mask",
|
||||
SyntaxShape::Any,
|
||||
"mask indicating insertions",
|
||||
Some('m'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Shifts the values by a given period",
|
||||
example: r#"let s = ([1 2 2 3 3] | dataframe to-df | dataframe shift 2);
|
||||
let mask = ($s | dataframe is-null);
|
||||
$s | dataframe set 0 --mask $mask"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let value: Value = args.req(0)?;
|
||||
let mask: Value = args.req_named("mask")?;
|
||||
|
||||
let mask_df = match &mask.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only use a series as mask",
|
||||
value.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let mask_series = mask_df.as_series(&mask.tag.span)?;
|
||||
|
||||
let bool_mask = match mask_series.dtype() {
|
||||
DataType::Boolean => mask_series
|
||||
.bool()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &mask.tag.span, None)),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only use bool series as mask",
|
||||
value.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Int(val)) => {
|
||||
let chunked = series.i64().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&value.tag.span,
|
||||
Some("The value has to match the set value type"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.set(bool_mask, Some(*val))
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
UntaggedValue::Primitive(Primitive::Decimal(val)) => {
|
||||
let chunked = series.as_ref().f64().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&value.tag.span,
|
||||
Some("The value has to match the series type"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.set(
|
||||
bool_mask,
|
||||
Some(
|
||||
val.to_f64()
|
||||
.expect("internal error: expected f64-compatible decimal"),
|
||||
),
|
||||
)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
UntaggedValue::Primitive(Primitive::String(val)) => {
|
||||
let chunked = series.as_ref().utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&value.tag.span,
|
||||
Some("The value has to match the series type"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.set(bool_mask, Some(val.as_ref()))
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None))?;
|
||||
|
||||
let mut res = res.into_series();
|
||||
res.rename("string");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
format!(
|
||||
"this value cannot be set in a series of type '{}'",
|
||||
series.dtype()
|
||||
),
|
||||
value.tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Primitive, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use polars::prelude::{ChunkSet, DataType, IntoSeries};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe set-with-idx"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Sets value in the given index"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe set-with-idx")
|
||||
.required("value", SyntaxShape::Any, "value to be inserted in series")
|
||||
.required_named(
|
||||
"indices",
|
||||
SyntaxShape::Any,
|
||||
"list of indices indicating where to set the value",
|
||||
Some('i'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Set value in selected rows from series",
|
||||
example: r#"let series = ([4 1 5 2 4 3] | dataframe to-df);
|
||||
let indices = ([0 2] | dataframe to-df);
|
||||
$series | dataframe set-with-idx 6 -i $indices"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(6).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let value: Value = args.req(0)?;
|
||||
let indices: Value = args.req_named("indices")?;
|
||||
|
||||
let indices = match &indices.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only use a series for set command",
|
||||
value.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let indices = indices.as_series(&value.tag.span)?;
|
||||
|
||||
let casted = match indices.dtype() {
|
||||
DataType::UInt32 | DataType::UInt64 | DataType::Int32 | DataType::Int64 => indices
|
||||
.as_ref()
|
||||
.cast(&DataType::UInt32)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None)),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Incorrect type",
|
||||
"Series with incorrect type",
|
||||
&value.tag.span,
|
||||
"Consider using a Series with type int type",
|
||||
&value.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let indices = casted
|
||||
.u32()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None))?
|
||||
.into_iter()
|
||||
.filter_map(|val| val.map(|v| v as usize));
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Int(val)) => {
|
||||
let chunked = series.i64().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&value.tag.span,
|
||||
Some("The value has to match the set value type"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.set_at_idx(indices, Some(*val))
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
UntaggedValue::Primitive(Primitive::Decimal(val)) => {
|
||||
let chunked = series.as_ref().f64().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&value.tag.span,
|
||||
Some("The value has to match the series type"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.set_at_idx(
|
||||
indices,
|
||||
Some(
|
||||
val.to_f64()
|
||||
.expect("internal error: expected f64-compatible decimal"),
|
||||
),
|
||||
)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
UntaggedValue::Primitive(Primitive::String(val)) => {
|
||||
let chunked = series.as_ref().utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&value.tag.span,
|
||||
Some("The value has to match the series type"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.set_at_idx(indices, Some(val.as_ref()))
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None))?;
|
||||
|
||||
let mut res = res.into_series();
|
||||
res.rename("string");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
format!(
|
||||
"this value cannot be set in a series of type '{}'",
|
||||
series.as_ref().dtype()
|
||||
),
|
||||
value.tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe shift"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Shifts the values by a given period"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe unique").required("period", SyntaxShape::Int, "shift period")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Shifts the values by a given period",
|
||||
example: "[1 2 2 3 3] | dataframe to-df | dataframe shift 2",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let period: Tagged<i64> = args.req(0)?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df.as_series(&df_tag.span)?.shift(period.item);
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user