Move nu-data out of nu-cli (#2369)

* WIP for moving nu-data out

* Refactor nu-data out of nu-cli

* Remove unwraps

* Remove unwraps
This commit is contained in:
Jonathan Turner 2020-08-18 19:00:02 +12:00 committed by GitHub
parent 1d5518a214
commit 738541f727
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 715 additions and 514 deletions

38
Cargo.lock generated
View File

@ -593,9 +593,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "chrono"
version = "0.4.13"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6"
checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b"
dependencies = [
"num-integer",
"num-traits 0.2.12",
@ -2932,6 +2932,7 @@ dependencies = [
"futures 0.3.5",
"log 0.4.11",
"nu-cli",
"nu-data",
"nu-errors",
"nu-parser",
"nu-plugin",
@ -3005,6 +3006,7 @@ dependencies = [
"log 0.4.11",
"meval",
"natural",
"nu-data",
"nu-errors",
"nu-parser",
"nu-plugin",
@ -3058,6 +3060,36 @@ dependencies = [
"zip",
]
[[package]]
name = "nu-data"
version = "0.18.2"
dependencies = [
"ansi_term 0.12.1",
"bigdecimal",
"byte-unit",
"chrono",
"derive-new",
"directories 2.0.2",
"dirs 2.0.2",
"getset",
"indexmap",
"log 0.4.11",
"nu-errors",
"nu-protocol",
"nu-source",
"nu-table",
"nu-value-ext",
"num-bigint",
"num-format",
"num-traits 0.2.12",
"parking_lot 0.11.0",
"query_interface",
"serde 1.0.115",
"toml 0.5.6",
"umask",
"users",
]
[[package]]
name = "nu-errors"
version = "0.18.2"
@ -3346,7 +3378,7 @@ version = "0.18.2"
dependencies = [
"ansi_term 0.12.1",
"bat",
"nu-cli",
"nu-data",
"nu-errors",
"nu-plugin",
"nu-protocol",

View File

@ -19,6 +19,7 @@ members = ["crates/*/"]
[dependencies]
nu-cli = {version = "0.18.2", path = "./crates/nu-cli"}
nu-data = {version = "0.18.2", path = "./crates/nu-data"}
nu-errors = {version = "0.18.2", path = "./crates/nu-errors"}
nu-parser = {version = "0.18.2", path = "./crates/nu-parser"}
nu-plugin = {version = "0.18.2", path = "./crates/nu-plugin"}
@ -101,7 +102,7 @@ s3 = ["nu_plugin_s3"]
clipboard-cli = ["nu-cli/clipboard-cli"]
ctrlc-support = ["nu-cli/ctrlc"]
directories-support = ["nu-cli/directories", "nu-cli/dirs"]
directories-support = ["nu-cli/directories", "nu-cli/dirs", "nu-data/directories", "nu-data/dirs"]
git-support = ["nu-cli/git2"]
ptree-support = ["nu-cli/ptree"]
term-support = ["nu-cli/term"]

View File

@ -10,6 +10,7 @@ version = "0.18.2"
doctest = false
[dependencies]
nu-data = {version = "0.18.2", path = "../nu-data"}
nu-errors = {version = "0.18.2", path = "../nu-errors"}
nu-parser = {version = "0.18.2", path = "../nu-parser"}
nu-plugin = {version = "0.18.2", path = "../nu-plugin"}

View File

@ -111,7 +111,7 @@ fn search_paths() -> Vec<std::path::PathBuf> {
}
}
if let Ok(config) = crate::data::config::config(Tag::unknown()) {
if let Ok(config) = nu_data::config::config(Tag::unknown()) {
if let Some(plugin_dirs) = config.get("plugin_dirs") {
if let Value {
value: UntaggedValue::Table(pipelines),
@ -210,7 +210,7 @@ impl History {
})
.unwrap_or_else(|_| PathBuf::from(FNAME));
let cfg = crate::data::config::config(Tag::unknown());
let cfg = nu_data::config::config(Tag::unknown());
if let Ok(c) = cfg {
match &c.get("history-path") {
Some(Value {
@ -471,7 +471,7 @@ pub async fn run_vec_of_pipelines(
}
// before we start up, let's run our startup commands
if let Ok(config) = crate::data::config::config(Tag::unknown()) {
if let Ok(config) = nu_data::config::config(Tag::unknown()) {
if let Some(commands) = config.get("startup") {
match commands {
Value {
@ -752,7 +752,7 @@ pub async fn cli(
let mut ctrlcbreak = false;
// before we start up, let's run our startup commands
if let Ok(config) = crate::data::config::config(Tag::unknown()) {
if let Ok(config) = nu_data::config::config(Tag::unknown()) {
if let Some(commands) = config.get("startup") {
match commands {
Value {

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::data::config;
use crate::prelude::*;
use nu_data::config;
use nu_errors::ShellError;
use nu_protocol::{
hir::Block, CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
@ -82,7 +82,7 @@ pub async fn alias(
let mut processed_args: Vec<String> = vec![];
if let Some(true) = save {
let mut result = crate::data::config::read(name.clone().tag, &None)?;
let mut result = nu_data::config::read(name.clone().tag, &None)?;
// process the alias to remove the --save flag
let left_brace = raw_input.find('{').unwrap_or(0);

View File

@ -1,10 +1,10 @@
use crate::commands::UnevaluatedCallInfo;
use crate::commands::WholeStreamCommand;
use crate::data::config::table::AutoPivotMode;
use crate::data::config::table::HasTableProperties;
use crate::data::config::NuConfig as Configuration;
use crate::data::value::format_leaf;
use crate::prelude::*;
use nu_data::config::table::AutoPivotMode;
use nu_data::config::table::HasTableProperties;
use nu_data::config::NuConfig as Configuration;
use nu_data::value::format_leaf;
use nu_errors::ShellError;
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
use nu_protocol::{Primitive, Scope, Signature, UntaggedValue, Value};

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_errors::ShellError;
use crate::commands::WholeStreamCommand;
use crate::data::value::format_leaf;
use nu_data::value::format_leaf;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)]

View File

@ -425,7 +425,7 @@ fn spawn(
};
if external_failed {
let cfg = crate::data::config::config(Tag::unknown());
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 {

View File

@ -45,7 +45,7 @@ pub async fn clear(
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = crate::data::config::read(name_span, &None)?;
let mut result = nu_data::config::read(name_span, &None)?;
result.clear();

View File

@ -27,7 +27,7 @@ impl WholeStreamCommand for Command {
) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let name = args.call_info.name_tag;
let result = crate::data::config::read(name_span, &None)?;
let result = nu_data::config::read(name_span, &None)?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),

View File

@ -56,7 +56,7 @@ pub async fn get(
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let result = crate::data::config::read(name_span, &None)?;
let result = nu_data::config::read(name_span, &None)?;
let key = get.to_string();
let value = result

View File

@ -50,7 +50,7 @@ pub async fn set(
let configuration = load.item().clone();
let result = crate::data::config::read(name_span, &Some(configuration))?;
let result = nu_data::config::read(name_span, &Some(configuration))?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),

View File

@ -54,7 +54,7 @@ pub async fn remove(
let name_span = args.call_info.name_tag.clone();
let (RemoveArgs { remove }, _) = args.process(&registry).await?;
let mut result = crate::data::config::read(name_span, &None)?;
let mut result = nu_data::config::read(name_span, &None)?;
let key = remove.to_string();

View File

@ -55,7 +55,7 @@ pub async fn set(
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = crate::data::config::read(name_span, &None)?;
let mut result = nu_data::config::read(name_span, &None)?;
result.insert(key.to_string(), value.clone());

View File

@ -58,7 +58,7 @@ pub async fn set_into(
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = crate::data::config::read(name_span, &None)?;
let mut result = nu_data::config::read(name_span, &None)?;
// In the original code, this is set to `Some` if the `load flag is set`
let configuration = None;

View File

@ -102,7 +102,7 @@ impl Iterator for RangeIterator {
if self.curr != self.end {
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
self.curr = match crate::data::value::compute_values(
self.curr = match nu_data::value::compute_values(
Operator::Plus,
&UntaggedValue::Primitive(self.curr.clone()),
&UntaggedValue::int(1),

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use crate::TaggedListBuilder;
use calamine::*;
use nu_data::TaggedListBuilder;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
use std::io::Cursor;

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use crate::TaggedListBuilder;
use calamine::*;
use nu_data::TaggedListBuilder;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
use std::io::Cursor;

View File

@ -179,7 +179,7 @@ pub async fn group_by(
None => as_string(row),
});
crate::utils::data::group(&values, &Some(block), &name)
nu_data::utils::group(&values, &Some(block), &name)
}
Grouper::ByColumn(column_name) => group(&column_name, &values, name),
};
@ -234,12 +234,12 @@ pub fn group(
}
});
crate::utils::data::group(&values, &Some(block), &name)
nu_data::utils::group(&values, &Some(block), &name)
}
Grouper::ByColumn(None) => {
let block = Box::new(move |_, row: &Value| as_string(row));
crate::utils::data::group(&values, &Some(block), &name)
nu_data::utils::group(&values, &Some(block), &name)
}
Grouper::ByBlock => Err(ShellError::unimplemented(
"Block not implemented: This should never happen.",
@ -250,7 +250,7 @@ pub fn group(
#[cfg(test)]
mod tests {
use super::group;
use crate::utils::data::helpers::{committers, date, int, row, string, table};
use nu_data::utils::helpers::{committers, date, int, row, string, table};
use nu_errors::ShellError;
use nu_source::*;

View File

@ -102,7 +102,7 @@ pub async fn group_by_date(
(Grouper::ByDate(None), GroupByColumn::Name(None)) => {
let block = Box::new(move |_, row: &Value| row.format("%Y-%m-%d"));
crate::utils::data::group(&values, &Some(block), &name)
nu_data::utils::group(&values, &Some(block), &name)
}
(Grouper::ByDate(None), GroupByColumn::Name(Some(column_name))) => {
let block = Box::new(move |_, row: &Value| {
@ -113,12 +113,12 @@ pub async fn group_by_date(
group_key?.format("%Y-%m-%d")
});
crate::utils::data::group(&values, &Some(block), &name)
nu_data::utils::group(&values, &Some(block), &name)
}
(Grouper::ByDate(Some(fmt)), GroupByColumn::Name(None)) => {
let block = Box::new(move |_, row: &Value| row.format(&fmt));
crate::utils::data::group(&values, &Some(block), &name)
nu_data::utils::group(&values, &Some(block), &name)
}
(Grouper::ByDate(Some(fmt)), GroupByColumn::Name(Some(column_name))) => {
let block = Box::new(move |_, row: &Value| {
@ -129,7 +129,7 @@ pub async fn group_by_date(
group_key?.format(&fmt)
});
crate::utils::data::group(&values, &Some(block), &name)
nu_data::utils::group(&values, &Some(block), &name)
}
};

View File

@ -1,10 +1,10 @@
use crate::commands::command::Command;
use crate::commands::WholeStreamCommand;
use crate::data::command_dict;
use crate::documentation::{generate_docs, get_documentation, DocumentationConfig};
use crate::prelude::*;
use nu_data::command::signature_dict;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
use nu_source::{SpannedItem, Tagged};
use nu_value_ext::get_data_by_key;
@ -38,6 +38,21 @@ impl WholeStreamCommand for Help {
}
}
pub(crate) 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()
}
async fn help(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let name = args.call_info.name_tag.clone();

View File

@ -107,9 +107,9 @@ pub async fn histogram(
"value".to_string().tagged(&name)
};
let results = crate::utils::data::report(
let results = nu_data::utils::report(
&UntaggedValue::table(&values).into_value(&name),
crate::utils::data::Operation {
nu_data::utils::Operation {
grouper: Some(Box::new(move |_, _| Ok(String::from("frequencies")))),
splitter: Some(splitter(column_grouper)),
format: None,

View File

@ -266,7 +266,7 @@ macro_rules! command {
Extract {
$($extract:tt)* {
use $crate::data::types::ExtractType;
use $nu_data::types::ExtractType;
let value = $args.expect_nth($($positional_count)*)?;
Block::extract(value)?
}
@ -321,7 +321,7 @@ macro_rules! command {
Extract {
$($extract:tt)* {
use $crate::data::types::ExtractType;
use $nu_data::types::ExtractType;
let value = $args.expect_nth($($positional_count)*)?;
<$param_kind>::extract(&value)?
}

View File

@ -118,7 +118,7 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
..
} => {
let left = UntaggedValue::from(Primitive::Int(num.into()));
let result = crate::data::value::compute_values(Operator::Divide, &left, &total_rows);
let result = nu_data::value::compute_values(Operator::Divide, &left, &total_rows);
match result {
Ok(UntaggedValue::Primitive(Primitive::Decimal(result))) => {
@ -142,7 +142,7 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
..
} => {
let left = UntaggedValue::from(other);
let result = crate::data::value::compute_values(Operator::Divide, &left, &total_rows);
let result = nu_data::value::compute_values(Operator::Divide, &left, &total_rows);
match result {
Ok(value) => Ok(value.into_value(name)),

View File

@ -138,7 +138,7 @@ fn compute_average(values: &[Value], name: impl Into<Tag>) -> Result<Value, Shel
..
} => {
let left = UntaggedValue::from(Primitive::Int(num.into()));
let result = crate::data::value::compute_values(Operator::Divide, &left, &total_rows);
let result = nu_data::value::compute_values(Operator::Divide, &left, &total_rows);
match result {
Ok(UntaggedValue::Primitive(Primitive::Decimal(result))) => {
@ -162,7 +162,7 @@ fn compute_average(values: &[Value], name: impl Into<Tag>) -> Result<Value, Shel
..
} => {
let left = UntaggedValue::from(other);
let result = crate::data::value::compute_values(Operator::Divide, &left, &total_rows);
let result = nu_data::value::compute_values(Operator::Divide, &left, &total_rows);
match result {
Ok(value) => Ok(value.into_value(name)),

View File

@ -1,4 +1,4 @@
use crate::data::value::{compare_values, compute_values};
use nu_data::value::{compare_values, compute_values};
use nu_errors::ShellError;
use nu_protocol::hir::Operator;
use nu_protocol::{UntaggedValue, Value};

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::data::value::compute_values;
use crate::prelude::*;
use bigdecimal::FromPrimitive;
use nu_data::value::compute_values;
use nu_errors::ShellError;
use nu_protocol::{
hir::Operator, Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value,

View File

@ -1,8 +1,8 @@
use crate::commands::classified::block::run_block;
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::data::value::merge_values;
use crate::prelude::*;
use nu_data::value::merge_values;
use indexmap::IndexMap;
use nu_errors::ShellError;

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::data::base::select_fields;
use crate::prelude::*;
use nu_data::base::select_fields;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, Value};
use nu_source::span_for_spanned_list;

View File

@ -1,7 +1,7 @@
use super::{operate, DefaultArguments};
use crate::commands::WholeStreamCommand;
use crate::data::files::get_file_type;
use crate::prelude::*;
use crate::shell::filesystem_shell::get_file_type;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use std::path::Path;

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::data::base::reject_fields;
use crate::prelude::*;
use nu_data::base::reject_fields;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
use nu_source::Tagged;

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::data::base::coerce_compare;
use crate::prelude::*;
use nu_data::base::coerce_compare;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;

View File

@ -86,12 +86,12 @@ pub fn split(
}
});
crate::utils::data::split(&values, &Some(block), &name)
nu_data::utils::split(&values, &Some(block), &name)
}
Grouper::ByColumn(None) => {
let block = Box::new(move |_, row: &Value| as_string(row));
crate::utils::data::split(&values, &Some(block), &name)
nu_data::utils::split(&values, &Some(block), &name)
}
}
}
@ -124,7 +124,7 @@ pub fn suggestions(tried: Tagged<&str>, for_value: &Value) -> ShellError {
#[cfg(test)]
mod tests {
use super::split;
use crate::utils::data::helpers::{committers_grouped_by_date, date, int, row, string, table};
use nu_data::utils::helpers::{committers_grouped_by_date, date, int, row, string, table};
use nu_protocol::UntaggedValue;
use nu_source::*;

View File

@ -1,8 +1,8 @@
use crate::commands::WholeStreamCommand;
use crate::data::config::table::HasTableProperties;
use crate::data::config::NuConfig as TableConfiguration;
use crate::data::value::{format_leaf, style_leaf};
use crate::prelude::*;
use nu_data::config::table::HasTableProperties;
use nu_data::config::NuConfig as TableConfiguration;
use nu_data::value::{format_leaf, style_leaf};
use nu_errors::ShellError;
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_table::{draw_table, Alignment, StyledString, TextStyle};

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::data::value::format_leaf;
use crate::prelude::*;
use futures::StreamExt;
use nu_data::value::format_leaf;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::{AnchorLocation, Tagged};

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::data::value::format_leaf;
use crate::prelude::*;
use futures::StreamExt;
use nu_data::value::format_leaf;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue, Value};

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use crate::TaggedListBuilder;
use indexmap::IndexMap;
use nu_data::TaggedListBuilder;
use nu_errors::ShellError;
use nu_protocol::{Dictionary, Signature, UntaggedValue};

View File

@ -1,12 +0,0 @@
pub(crate) mod base;
pub(crate) mod command;
pub mod config;
pub(crate) mod dict;
pub(crate) mod files;
pub mod primitive;
pub(crate) mod types;
pub mod value;
pub(crate) use command::command_dict;
pub(crate) use dict::TaggedListBuilder;
pub(crate) use files::dir_entry_dict;

View File

@ -1,164 +0,0 @@
mod conf;
mod nuconfig;
pub mod table;
#[cfg(test)]
pub mod tests;
pub(crate) use conf::Conf;
pub(crate) use nuconfig::NuConfig;
use crate::commands::from_toml::convert_toml_value_to_nu_value;
use crate::commands::to_toml::value_to_toml_value;
use crate::prelude::*;
use indexmap::IndexMap;
use log::trace;
use nu_errors::ShellError;
use nu_protocol::{Dictionary, ShellTypeName, UntaggedValue, Value};
use nu_source::Tag;
use std::fs::{self, OpenOptions};
use std::io;
use std::path::{Path, PathBuf};
#[cfg(feature = "directories")]
pub fn config_path() -> Result<PathBuf, ShellError> {
use directories::ProjectDirs;
let dir = ProjectDirs::from("org", "nushell", "nu")
.ok_or_else(|| ShellError::untagged_runtime_error("Couldn't find project directory"))?;
let path = ProjectDirs::config_dir(&dir).to_owned();
std::fs::create_dir_all(&path).map_err(|err| {
ShellError::untagged_runtime_error(&format!("Couldn't create {} path:\n{}", "config", err))
})?;
Ok(path)
}
#[cfg(not(feature = "directories"))]
pub fn config_path() -> Result<PathBuf, ShellError> {
// FIXME: unsure if this should be error or a simple default
Ok(std::path::PathBuf::from("/"))
}
pub fn default_path() -> Result<PathBuf, ShellError> {
default_path_for(&None)
}
pub fn default_path_for(file: &Option<PathBuf>) -> Result<PathBuf, ShellError> {
let mut filename = config_path()?;
let file: &Path = file
.as_ref()
.map(AsRef::as_ref)
.unwrap_or_else(|| "config.toml".as_ref());
filename.push(file);
Ok(filename)
}
#[cfg(feature = "directories")]
pub fn user_data() -> Result<PathBuf, ShellError> {
use directories::ProjectDirs;
let dir = ProjectDirs::from("org", "nushell", "nu")
.ok_or_else(|| ShellError::untagged_runtime_error("Couldn't find project directory"))?;
let path = ProjectDirs::data_local_dir(&dir).to_owned();
std::fs::create_dir_all(&path).map_err(|err| {
ShellError::untagged_runtime_error(&format!(
"Couldn't create {} path:\n{}",
"user data", err
))
})?;
Ok(path)
}
#[cfg(not(feature = "directories"))]
pub fn user_data() -> Result<PathBuf, ShellError> {
// FIXME: unsure if this should be error or a simple default
Ok(std::path::PathBuf::from("/"))
}
pub fn read(
tag: impl Into<Tag>,
at: &Option<PathBuf>,
) -> Result<IndexMap<String, Value>, ShellError> {
let filename = default_path()?;
let filename = match at {
None => filename,
Some(ref file) => file.clone(),
};
if !filename.exists() && touch(&filename).is_err() {
// If we can't create configs, let's just return an empty indexmap instead as we may be in
// a readonly environment
return Ok(IndexMap::new());
}
trace!("config file = {}", filename.display());
let tag = tag.into();
let contents = fs::read_to_string(filename)
.map(|v| v.tagged(&tag))
.map_err(|err| {
ShellError::labeled_error(
&format!("Couldn't read config file:\n{}", err),
"file name",
&tag,
)
})?;
let parsed: toml::Value = toml::from_str(&contents).map_err(|err| {
ShellError::labeled_error(
&format!("Couldn't parse config file:\n{}", err),
"file name",
&tag,
)
})?;
let value = convert_toml_value_to_nu_value(&parsed, tag);
let tag = value.tag();
match value.value {
UntaggedValue::Row(Dictionary { entries }) => Ok(entries),
other => Err(ShellError::type_error(
"Dictionary",
other.type_name().spanned(tag.span),
)),
}
}
pub fn config(tag: impl Into<Tag>) -> Result<IndexMap<String, Value>, ShellError> {
read(tag, &None)
}
pub fn write(config: &IndexMap<String, Value>, at: &Option<PathBuf>) -> Result<(), ShellError> {
let filename = &mut default_path()?;
let filename = match at {
None => filename,
Some(file) => {
filename.pop();
filename.push(file);
filename
}
};
let contents = value_to_toml_value(
&UntaggedValue::Row(Dictionary::new(config.clone())).into_untagged_value(),
)?;
let contents = toml::to_string(&contents)?;
fs::write(&filename, &contents)?;
Ok(())
}
// A simple implementation of `% touch path` (ignores existing files)
fn touch(path: &Path) -> io::Result<()> {
match OpenOptions::new().create(true).write(true).open(path) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}

View File

@ -1,199 +0,0 @@
use crate::commands::du::{DirBuilder, DirInfo};
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value};
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
pub(crate) fn get_file_type(md: &std::fs::Metadata) -> &str {
let ft = md.file_type();
let mut file_type = "Unknown";
if ft.is_dir() {
file_type = "Dir";
} else if ft.is_file() {
file_type = "File";
} else if ft.is_symlink() {
file_type = "Symlink";
} else {
#[cfg(unix)]
{
if ft.is_block_device() {
file_type = "Block device";
} else if ft.is_char_device() {
file_type = "Char device";
} else if ft.is_fifo() {
file_type = "Pipe";
} else if ft.is_socket() {
file_type = "Socket";
}
}
}
file_type
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn dir_entry_dict(
filename: &std::path::Path,
metadata: Option<&std::fs::Metadata>,
tag: impl Into<Tag>,
long: bool,
short_name: bool,
du: bool,
ctrl_c: Arc<AtomicBool>,
) -> Result<Value, ShellError> {
let tag = tag.into();
let mut dict = TaggedDictBuilder::new(&tag);
// Insert all columns first to maintain proper table alignment if we can't find (or are not allowed to view) any information
if long {
#[cfg(windows)]
{
for column in [
"name", "type", "target", "readonly", "size", "created", "accessed", "modified",
]
.iter()
{
dict.insert_untagged(*column, UntaggedValue::nothing());
}
}
#[cfg(unix)]
{
for column in [
"name", "type", "target", "readonly", "mode", "uid", "group", "size", "created",
"accessed", "modified",
]
.iter()
{
dict.insert_untagged(&(*column.to_owned()), UntaggedValue::nothing());
}
}
} else {
for column in ["name", "type", "target", "size", "modified"].iter() {
if *column == "target" {
continue;
}
dict.insert_untagged(*column, UntaggedValue::nothing());
}
}
let name = if short_name {
filename.file_name().and_then(|s| s.to_str())
} else {
filename.to_str()
}
.ok_or_else(|| {
ShellError::labeled_error(
format!("Invalid file name: {:}", filename.to_string_lossy()),
"invalid file name",
tag,
)
})?;
dict.insert_untagged("name", UntaggedValue::string(name));
if let Some(md) = metadata {
dict.insert_untagged("type", get_file_type(md));
}
if long {
if let Some(md) = metadata {
if md.file_type().is_symlink() {
let symlink_target_untagged_value: UntaggedValue;
if let Ok(path_to_link) = filename.read_link() {
symlink_target_untagged_value =
UntaggedValue::string(path_to_link.to_string_lossy());
} else {
symlink_target_untagged_value =
UntaggedValue::string("Could not obtain target file's path");
}
dict.insert_untagged("target", symlink_target_untagged_value);
}
}
}
if long {
if let Some(md) = metadata {
dict.insert_untagged(
"readonly",
UntaggedValue::boolean(md.permissions().readonly()),
);
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
use std::os::unix::fs::PermissionsExt;
let mode = md.permissions().mode();
dict.insert_untagged(
"mode",
UntaggedValue::string(umask::Mode::from(mode).to_string()),
);
if let Some(user) = users::get_user_by_uid(md.uid()) {
dict.insert_untagged(
"uid",
UntaggedValue::string(user.name().to_string_lossy()),
);
}
if let Some(group) = users::get_group_by_gid(md.gid()) {
dict.insert_untagged(
"group",
UntaggedValue::string(group.name().to_string_lossy()),
);
}
}
}
}
if let Some(md) = metadata {
let mut size_untagged_value: UntaggedValue = UntaggedValue::nothing();
if md.is_dir() {
let dir_size: u64 = if du {
let params = DirBuilder::new(
Tag {
anchor: None,
span: Span::new(0, 2),
},
None,
false,
None,
false,
);
DirInfo::new(filename, &params, None, ctrl_c).get_size()
} else {
md.len()
};
size_untagged_value = UntaggedValue::filesize(dir_size);
} else if md.is_file() {
size_untagged_value = UntaggedValue::filesize(md.len());
} else if md.file_type().is_symlink() {
if let Ok(symlink_md) = filename.symlink_metadata() {
size_untagged_value = UntaggedValue::filesize(symlink_md.len() as u64);
}
}
dict.insert_untagged("size", size_untagged_value);
}
if let Some(md) = metadata {
if long {
if let Ok(c) = md.created() {
dict.insert_untagged("created", UntaggedValue::system_date(c));
}
if let Ok(a) = md.accessed() {
dict.insert_untagged("accessed", UntaggedValue::system_date(a));
}
}
if let Ok(m) = md.modified() {
dict.insert_untagged("modified", UntaggedValue::system_date(m));
}
}
Ok(dict.into_value())
}

View File

@ -1,6 +1,6 @@
use crate::data::config::Conf;
use crate::env::directory_specific_environment::*;
use indexmap::{indexmap, IndexSet};
use nu_data::config::Conf;
use nu_errors::ShellError;
use nu_protocol::{UntaggedValue, Value};
use std::env::*;
@ -154,7 +154,7 @@ impl Env for Environment {
#[cfg(test)]
mod tests {
use super::{Env, Environment};
use crate::data::config::{tests::FakeConfig, Conf};
use nu_data::config::{tests::FakeConfig, Conf};
use nu_protocol::UntaggedValue;
use nu_test_support::fs::Stub::FileWithContent;
use nu_test_support::playground::Playground;

View File

@ -1,5 +1,5 @@
use crate::context::Context;
use crate::data::config::{Conf, NuConfig};
use nu_data::config::{Conf, NuConfig};
use crate::env::environment::{Env, Environment};
use nu_source::Text;
@ -128,9 +128,9 @@ impl EnvironmentSyncer {
mod tests {
use super::EnvironmentSyncer;
use crate::context::Context;
use crate::data::config::tests::FakeConfig;
use crate::env::environment::Env;
use indexmap::IndexMap;
use nu_data::config::tests::FakeConfig;
use nu_errors::ShellError;
use nu_test_support::fs::Stub::FileWithContent;
use nu_test_support::playground::Playground;

View File

@ -1,4 +1,4 @@
use crate::data::value;
use nu_data::value;
use nu_errors::ShellError;
use nu_protocol::hir::Operator;
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};

View File

@ -17,7 +17,7 @@ pub fn nu(env: &IndexMap<String, String>, tag: impl Into<Tag>) -> Result<Value,
}
nu_dict.insert_value("env", dict.into_value());
let config = crate::data::config::read(&tag, &None)?;
let config = nu_data::config::read(&tag, &None)?;
nu_dict.insert_value("config", UntaggedValue::row(config).into_value(&tag));
let mut table = vec![];
@ -39,7 +39,7 @@ pub fn nu(env: &IndexMap<String, String>, tag: impl Into<Tag>) -> Result<Value,
let temp = std::env::temp_dir();
nu_dict.insert_value("temp-dir", UntaggedValue::path(temp).into_value(&tag));
let config = crate::data::config::default_path()?;
let config = nu_data::config::default_path()?;
nu_dict.insert_value("config-path", UntaggedValue::path(config).into_value(&tag));
let keybinding_path = crate::keybinding::keybinding_path()?;

View File

@ -406,7 +406,7 @@ pub struct Keybinding {
type Keybindings = Vec<Keybinding>;
pub(crate) fn keybinding_path() -> Result<std::path::PathBuf, nu_errors::ShellError> {
crate::data::config::default_path_for(&Some(std::path::PathBuf::from("keybindings.yml")))
nu_data::config::default_path_for(&Some(std::path::PathBuf::from("keybindings.yml")))
}
pub(crate) fn load_keybindings(

View File

@ -17,7 +17,6 @@ mod cli;
mod commands;
mod completion;
mod context;
pub mod data;
mod deserializer;
mod documentation;
mod env;
@ -43,14 +42,14 @@ pub use crate::commands::command::{
};
pub use crate::commands::help::get_help;
pub use crate::context::{CommandRegistry, Context};
pub use crate::data::config;
pub use crate::data::dict::TaggedListBuilder;
pub use crate::data::primitive;
pub use crate::data::value;
pub use crate::env::environment_syncer::EnvironmentSyncer;
pub use crate::env::host::BasicHost;
pub use crate::prelude::ToOutputStream;
pub use crate::stream::{InputStream, InterruptibleStream, OutputStream};
pub use nu_data::config;
pub use nu_data::dict::TaggedListBuilder;
pub use nu_data::primitive;
pub use nu_data::value;
pub use nu_value_ext::ValueExt;
pub use num_traits::cast::ToPrimitive;

View File

@ -75,8 +75,8 @@ pub(crate) use crate::commands::command::{CommandArgs, RawCommandArgs, RunnableC
pub(crate) use crate::commands::Example;
pub(crate) use crate::context::CommandRegistry;
pub(crate) use crate::context::Context;
pub(crate) use crate::data::config;
pub(crate) use crate::data::value;
pub(crate) use nu_data::config;
pub(crate) use nu_data::value;
// pub(crate) use crate::env::host::handle_unexpected;
pub(crate) use crate::env::Host;
pub(crate) use crate::shell::filesystem_shell::FilesystemShell;
@ -87,7 +87,6 @@ pub(crate) use crate::stream::{InputStream, InterruptibleStream, OutputStream};
pub(crate) use bigdecimal::BigDecimal;
pub(crate) use futures::stream::BoxStream;
pub(crate) use futures::{Stream, StreamExt};
pub(crate) use nu_protocol::MaybeOwned;
pub(crate) use nu_source::{
b, AnchorLocation, DebugDocBuilder, PrettyDebug, PrettyDebugWithSource, Span, SpannedItem, Tag,
TaggedItem, Text,

View File

@ -13,8 +13,8 @@ use ichwh::{IchwhError, IchwhResult};
use crate::completion::{self, Completer};
use crate::context;
use crate::data::config;
use crate::prelude::*;
use nu_data::config;
pub(crate) struct NuCompleter {
file_completer: FilenameCompleter,

View File

@ -1,15 +1,16 @@
use crate::commands::cd::CdArgs;
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
use crate::commands::cp::CopyArgs;
use crate::commands::du::{DirBuilder, DirInfo};
use crate::commands::ls::LsArgs;
use crate::commands::mkdir::MkdirArgs;
use crate::commands::move_::mv::Arguments as MvArgs;
use crate::commands::rm::RemoveArgs;
use crate::data::dir_entry_dict;
use crate::path::canonicalize;
use crate::prelude::*;
use crate::shell::shell::Shell;
use crate::utils::FileStructure;
use nu_protocol::{TaggedDictBuilder, Value};
use std::collections::HashMap;
use std::io::{Error, ErrorKind};
@ -790,3 +791,197 @@ fn is_hidden_dir(dir: impl AsRef<Path>) -> bool {
.unwrap_or(false)
}
}
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
pub(crate) fn get_file_type(md: &std::fs::Metadata) -> &str {
let ft = md.file_type();
let mut file_type = "Unknown";
if ft.is_dir() {
file_type = "Dir";
} else if ft.is_file() {
file_type = "File";
} else if ft.is_symlink() {
file_type = "Symlink";
} else {
#[cfg(unix)]
{
if ft.is_block_device() {
file_type = "Block device";
} else if ft.is_char_device() {
file_type = "Char device";
} else if ft.is_fifo() {
file_type = "Pipe";
} else if ft.is_socket() {
file_type = "Socket";
}
}
}
file_type
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn dir_entry_dict(
filename: &std::path::Path,
metadata: Option<&std::fs::Metadata>,
tag: impl Into<Tag>,
long: bool,
short_name: bool,
du: bool,
ctrl_c: Arc<AtomicBool>,
) -> Result<Value, ShellError> {
let tag = tag.into();
let mut dict = TaggedDictBuilder::new(&tag);
// Insert all columns first to maintain proper table alignment if we can't find (or are not allowed to view) any information
if long {
#[cfg(windows)]
{
for column in [
"name", "type", "target", "readonly", "size", "created", "accessed", "modified",
]
.iter()
{
dict.insert_untagged(*column, UntaggedValue::nothing());
}
}
#[cfg(unix)]
{
for column in [
"name", "type", "target", "readonly", "mode", "uid", "group", "size", "created",
"accessed", "modified",
]
.iter()
{
dict.insert_untagged(&(*column.to_owned()), UntaggedValue::nothing());
}
}
} else {
for column in ["name", "type", "target", "size", "modified"].iter() {
if *column == "target" {
continue;
}
dict.insert_untagged(*column, UntaggedValue::nothing());
}
}
let name = if short_name {
filename.file_name().and_then(|s| s.to_str())
} else {
filename.to_str()
}
.ok_or_else(|| {
ShellError::labeled_error(
format!("Invalid file name: {:}", filename.to_string_lossy()),
"invalid file name",
tag,
)
})?;
dict.insert_untagged("name", UntaggedValue::string(name));
if let Some(md) = metadata {
dict.insert_untagged("type", get_file_type(md));
}
if long {
if let Some(md) = metadata {
if md.file_type().is_symlink() {
let symlink_target_untagged_value: UntaggedValue;
if let Ok(path_to_link) = filename.read_link() {
symlink_target_untagged_value =
UntaggedValue::string(path_to_link.to_string_lossy());
} else {
symlink_target_untagged_value =
UntaggedValue::string("Could not obtain target file's path");
}
dict.insert_untagged("target", symlink_target_untagged_value);
}
}
}
if long {
if let Some(md) = metadata {
dict.insert_untagged(
"readonly",
UntaggedValue::boolean(md.permissions().readonly()),
);
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
let mode = md.permissions().mode();
dict.insert_untagged(
"mode",
UntaggedValue::string(umask::Mode::from(mode).to_string()),
);
if let Some(user) = users::get_user_by_uid(md.uid()) {
dict.insert_untagged(
"uid",
UntaggedValue::string(user.name().to_string_lossy()),
);
}
if let Some(group) = users::get_group_by_gid(md.gid()) {
dict.insert_untagged(
"group",
UntaggedValue::string(group.name().to_string_lossy()),
);
}
}
}
}
if let Some(md) = metadata {
let mut size_untagged_value: UntaggedValue = UntaggedValue::nothing();
if md.is_dir() {
let dir_size: u64 = if du {
let params = DirBuilder::new(
Tag {
anchor: None,
span: Span::new(0, 2),
},
None,
false,
None,
false,
);
DirInfo::new(filename, &params, None, ctrl_c).get_size()
} else {
md.len()
};
size_untagged_value = UntaggedValue::filesize(dir_size);
} else if md.is_file() {
size_untagged_value = UntaggedValue::filesize(md.len());
} else if md.file_type().is_symlink() {
if let Ok(symlink_md) = filename.symlink_metadata() {
size_untagged_value = UntaggedValue::filesize(symlink_md.len() as u64);
}
}
dict.insert_untagged("size", size_untagged_value);
}
if let Some(md) = metadata {
if long {
if let Ok(c) = md.created() {
dict.insert_untagged("created", UntaggedValue::system_date(c));
}
if let Ok(a) = md.accessed() {
dict.insert_untagged("accessed", UntaggedValue::system_date(a));
}
}
if let Ok(m) = md.modified() {
dict.insert_untagged("modified", UntaggedValue::system_date(m));
}
}
Ok(dict.into_value())
}

View File

@ -1,12 +1,12 @@
use crate::commands::cd::CdArgs;
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
use crate::commands::cp::CopyArgs;
use crate::commands::help::command_dict;
use crate::commands::ls::LsArgs;
use crate::commands::mkdir::MkdirArgs;
use crate::commands::move_::mv::Arguments as MvArgs;
use crate::commands::rm::RemoveArgs;
use crate::completion;
use crate::data::command_dict;
use crate::prelude::*;
use crate::shell::shell::Shell;

View File

@ -1,4 +1,3 @@
pub mod data;
pub mod test_bins;
use crate::path::canonicalize;

40
crates/nu-data/Cargo.toml Normal file
View File

@ -0,0 +1,40 @@
[package]
authors = ["The Nu Project Contributors"]
description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-data"
version = "0.18.2"
[lib]
doctest = false
[dependencies]
ansi_term = "0.12.1"
bigdecimal = "0.1.2"
byte-unit = "3.1.3"
chrono = "0.4.15"
derive-new = "0.5.8"
directories = {version = "2.0.2", optional = true}
dirs = {version = "2.0.2", optional = true}
getset = "0.1.1"
indexmap = {version = "1.4.0", features = ["serde-1"]}
log = "0.4.8"
num-bigint = {version = "0.2.6", features = ["serde"]}
num-format = {version = "0.4", features = ["with-num-bigint"]}
num-traits = "0.2.11"
parking_lot = "0.11.0"
query_interface = "0.3.5"
serde = {version = "1.0.114", features = ["derive"]}
toml = "0.5.6"
umask = "1.0.0"
nu-errors = {version = "0.18.2", path = "../nu-errors"}
nu-protocol = {version = "0.18.2", path = "../nu-protocol"}
nu-source = {version = "0.18.2", path = "../nu-source"}
nu-table = {version = "0.18.2", path = "../nu-table"}
nu-value-ext = {version = "0.18.2", path = "../nu-value-ext"}
[target.'cfg(unix)'.dependencies]
users = "0.10.0"

View File

@ -49,7 +49,7 @@ impl std::convert::TryFrom<Option<&Value>> for Switch {
}
}
pub(crate) fn select_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Value {
pub fn select_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Value {
let mut out = TaggedDictBuilder::new(tag);
let descs = obj.data_descriptors();
@ -64,7 +64,7 @@ pub(crate) fn select_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>)
out.into_value()
}
pub(crate) fn reject_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Value {
pub fn reject_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Value {
let mut out = TaggedDictBuilder::new(tag);
let descs = obj.data_descriptors();
@ -80,7 +80,7 @@ pub(crate) fn reject_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>)
out.into_value()
}
pub(crate) enum CompareValues {
pub enum CompareValues {
Ints(BigInt, BigInt),
Decimals(BigDecimal, BigDecimal),
String(String, String),
@ -113,7 +113,7 @@ impl CompareValues {
}
}
pub(crate) fn coerce_compare(
pub fn coerce_compare(
left: &UntaggedValue,
right: &UntaggedValue,
) -> Result<CompareValues, (&'static str, &'static str)> {
@ -165,7 +165,7 @@ fn coerce_compare_primitive(
}
#[cfg(test)]
mod tests {
use indexmap::IndexMap;
use indexmap::{indexmap, IndexMap};
use nu_errors::ShellError;
use nu_protocol::{ColumnPath as ColumnPathValue, PathMember, UntaggedValue, Value};
use nu_source::*;
@ -173,19 +173,19 @@ mod tests {
use num_bigint::BigInt;
fn string(input: impl Into<String>) -> Value {
crate::utils::data::helpers::string(input)
crate::utils::helpers::string(input)
}
fn int(input: impl Into<BigInt>) -> Value {
crate::utils::data::helpers::int(input)
crate::utils::helpers::int(input)
}
fn row(entries: IndexMap<String, Value>) -> Value {
crate::utils::data::helpers::row(entries)
crate::utils::helpers::row(entries)
}
fn table(list: &[Value]) -> Value {
crate::utils::data::helpers::table(list)
crate::utils::helpers::table(list)
}
fn error_callback(

View File

@ -1,8 +1,9 @@
use crate::prelude::*;
use bigdecimal::BigDecimal;
use chrono::{DateTime, Utc};
use nu_protocol::RangeInclusion;
use nu_protocol::{format_primitive, ColumnPath, Dictionary, Primitive, UntaggedValue, Value};
use nu_source::{b, PrettyDebug};
use nu_source::{b, DebugDocBuilder, PrettyDebug};
use num_bigint::BigInt;
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::hash::Hash;

View File

@ -1,23 +1,8 @@
use crate::commands::command::Command;
use crate::data::TaggedListBuilder;
use crate::prelude::*;
use crate::TaggedListBuilder;
use nu_source::Tag;
use nu_protocol::{NamedType, PositionalType, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub(crate) 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()
}
fn for_spec(name: &str, ty: &str, required: bool, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
@ -33,7 +18,7 @@ fn for_spec(name: &str, ty: &str, required: bool, tag: impl Into<Tag>) -> Value
spec.into_value()
}
fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let mut sig = TaggedListBuilder::new(&tag);

View File

@ -0,0 +1,289 @@
mod conf;
mod nuconfig;
pub mod table;
pub mod tests;
pub use conf::Conf;
pub use nuconfig::NuConfig;
use indexmap::IndexMap;
use log::trace;
use nu_errors::{CoerceInto, ShellError};
use nu_protocol::{
Dictionary, Primitive, ShellTypeName, TaggedDictBuilder, UnspannedPathMember, UntaggedValue,
Value,
};
use nu_source::{SpannedItem, Tag, TaggedItem};
use std::fs::{self, OpenOptions};
use std::io;
use std::path::{Path, PathBuf};
pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
match v {
toml::Value::Boolean(b) => UntaggedValue::boolean(*b).into_value(tag),
toml::Value::Integer(n) => UntaggedValue::int(*n).into_value(tag),
toml::Value::Float(n) => UntaggedValue::decimal(*n).into_value(tag),
toml::Value::String(s) => {
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(tag)
}
toml::Value::Array(a) => UntaggedValue::Table(
a.iter()
.map(|x| convert_toml_value_to_nu_value(x, &tag))
.collect(),
)
.into_value(tag),
toml::Value::Datetime(dt) => {
UntaggedValue::Primitive(Primitive::String(dt.to_string())).into_value(tag)
}
toml::Value::Table(t) => {
let mut collected = TaggedDictBuilder::new(&tag);
for (k, v) in t.iter() {
collected.insert_value(k.clone(), convert_toml_value_to_nu_value(v, &tag));
}
collected.into_value()
}
}
}
fn collect_values(input: &[Value]) -> Result<Vec<toml::Value>, ShellError> {
let mut out = vec![];
for value in input {
out.push(helper(value)?);
}
Ok(out)
}
// Helper method to recursively convert nu_protocol::Value -> toml::Value
// This shouldn't be called at the top-level
fn helper(v: &Value) -> Result<toml::Value, ShellError> {
Ok(match &v.value {
UntaggedValue::Primitive(Primitive::Boolean(b)) => toml::Value::Boolean(*b),
UntaggedValue::Primitive(Primitive::Filesize(b)) => toml::Value::Integer(*b as i64),
UntaggedValue::Primitive(Primitive::Duration(i)) => toml::Value::String(i.to_string()),
UntaggedValue::Primitive(Primitive::Date(d)) => toml::Value::String(d.to_string()),
UntaggedValue::Primitive(Primitive::EndOfStream) => {
toml::Value::String("<End of Stream>".to_string())
}
UntaggedValue::Primitive(Primitive::BeginningOfStream) => {
toml::Value::String("<Beginning of Stream>".to_string())
}
UntaggedValue::Primitive(Primitive::Decimal(f)) => {
toml::Value::Float(f.tagged(&v.tag).coerce_into("converting to TOML float")?)
}
UntaggedValue::Primitive(Primitive::Int(i)) => {
toml::Value::Integer(i.tagged(&v.tag).coerce_into("converting to TOML integer")?)
}
UntaggedValue::Primitive(Primitive::Nothing) => {
toml::Value::String("<Nothing>".to_string())
}
UntaggedValue::Primitive(Primitive::Pattern(s)) => toml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::Line(s)) => toml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::Path(s)) => {
toml::Value::String(s.display().to_string())
}
UntaggedValue::Primitive(Primitive::ColumnPath(path)) => toml::Value::Array(
path.iter()
.map(|x| match &x.unspanned {
UnspannedPathMember::String(string) => Ok(toml::Value::String(string.clone())),
UnspannedPathMember::Int(int) => Ok(toml::Value::Integer(
int.tagged(&v.tag)
.coerce_into("converting to TOML integer")?,
)),
})
.collect::<Result<Vec<toml::Value>, ShellError>>()?,
),
UntaggedValue::Table(l) => toml::Value::Array(collect_values(l)?),
UntaggedValue::Error(e) => return Err(e.clone()),
UntaggedValue::Block(_) => toml::Value::String("<Block>".to_string()),
UntaggedValue::Primitive(Primitive::Range(_)) => toml::Value::String("<Range>".to_string()),
UntaggedValue::Primitive(Primitive::Binary(b)) => {
toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect())
}
UntaggedValue::Row(o) => {
let mut m = toml::map::Map::new();
for (k, v) in o.entries.iter() {
m.insert(k.clone(), helper(v)?);
}
toml::Value::Table(m)
}
})
}
/// Converts a nu_protocol::Value into a toml::Value
/// Will return a Shell Error, if the Nu Value is not a valid top-level TOML Value
pub fn value_to_toml_value(v: &Value) -> Result<toml::Value, ShellError> {
match &v.value {
UntaggedValue::Row(o) => {
let mut m = toml::map::Map::new();
for (k, v) in o.entries.iter() {
m.insert(k.clone(), helper(v)?);
}
Ok(toml::Value::Table(m))
}
UntaggedValue::Primitive(Primitive::String(s)) => {
// Attempt to de-serialize the String
toml::de::from_str(s).map_err(|_| {
ShellError::labeled_error(
format!("{:?} unable to de-serialize string to TOML", s),
"invalid TOML",
v.tag(),
)
})
}
_ => Err(ShellError::labeled_error(
format!("{:?} is not a valid top-level TOML", v.value),
"invalid TOML",
v.tag(),
)),
}
}
#[cfg(feature = "directories")]
pub fn config_path() -> Result<PathBuf, ShellError> {
use directories::ProjectDirs;
let dir = ProjectDirs::from("org", "nushell", "nu")
.ok_or_else(|| ShellError::untagged_runtime_error("Couldn't find project directory"))?;
let path = ProjectDirs::config_dir(&dir).to_owned();
std::fs::create_dir_all(&path).map_err(|err| {
ShellError::untagged_runtime_error(&format!("Couldn't create {} path:\n{}", "config", err))
})?;
Ok(path)
}
#[cfg(not(feature = "directories"))]
pub fn config_path() -> Result<PathBuf, ShellError> {
// FIXME: unsure if this should be error or a simple default
Ok(std::path::PathBuf::from("/"))
}
pub fn default_path() -> Result<PathBuf, ShellError> {
default_path_for(&None)
}
pub fn default_path_for(file: &Option<PathBuf>) -> Result<PathBuf, ShellError> {
let mut filename = config_path()?;
let file: &Path = file
.as_ref()
.map(AsRef::as_ref)
.unwrap_or_else(|| "config.toml".as_ref());
filename.push(file);
Ok(filename)
}
#[cfg(feature = "directories")]
pub fn user_data() -> Result<PathBuf, ShellError> {
use directories::ProjectDirs;
let dir = ProjectDirs::from("org", "nushell", "nu")
.ok_or_else(|| ShellError::untagged_runtime_error("Couldn't find project directory"))?;
let path = ProjectDirs::data_local_dir(&dir).to_owned();
std::fs::create_dir_all(&path).map_err(|err| {
ShellError::untagged_runtime_error(&format!(
"Couldn't create {} path:\n{}",
"user data", err
))
})?;
Ok(path)
}
#[cfg(not(feature = "directories"))]
pub fn user_data() -> Result<PathBuf, ShellError> {
// FIXME: unsure if this should be error or a simple default
Ok(std::path::PathBuf::from("/"))
}
pub fn read(
tag: impl Into<Tag>,
at: &Option<PathBuf>,
) -> Result<IndexMap<String, Value>, ShellError> {
let filename = default_path()?;
let filename = match at {
None => filename,
Some(ref file) => file.clone(),
};
if !filename.exists() && touch(&filename).is_err() {
// If we can't create configs, let's just return an empty indexmap instead as we may be in
// a readonly environment
return Ok(IndexMap::new());
}
trace!("config file = {}", filename.display());
let tag = tag.into();
let contents = fs::read_to_string(filename)
.map(|v| v.tagged(&tag))
.map_err(|err| {
ShellError::labeled_error(
&format!("Couldn't read config file:\n{}", err),
"file name",
&tag,
)
})?;
let parsed: toml::Value = toml::from_str(&contents).map_err(|err| {
ShellError::labeled_error(
&format!("Couldn't parse config file:\n{}", err),
"file name",
&tag,
)
})?;
let value = convert_toml_value_to_nu_value(&parsed, tag);
let tag = value.tag();
match value.value {
UntaggedValue::Row(Dictionary { entries }) => Ok(entries),
other => Err(ShellError::type_error(
"Dictionary",
other.type_name().spanned(tag.span),
)),
}
}
pub fn config(tag: impl Into<Tag>) -> Result<IndexMap<String, Value>, ShellError> {
read(tag, &None)
}
pub fn write(config: &IndexMap<String, Value>, at: &Option<PathBuf>) -> Result<(), ShellError> {
let filename = &mut default_path()?;
let filename = match at {
None => filename,
Some(file) => {
filename.pop();
filename.push(file);
filename
}
};
let contents = value_to_toml_value(
&UntaggedValue::Row(Dictionary::new(config.clone())).into_untagged_value(),
)?;
let contents = toml::to_string(&contents)?;
fs::write(&filename, &contents)?;
Ok(())
}
// A simple implementation of `% touch path` (ignores existing files)
fn touch(path: &Path) -> io::Result<()> {
match OpenOptions::new().create(true).write(true).open(path) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}

View File

@ -1,4 +1,4 @@
use crate::data::config::{read, Conf};
use crate::config::{read, Conf};
use indexmap::IndexMap;
use nu_protocol::Value;
use nu_source::Tag;

View File

@ -1,4 +1,4 @@
use crate::data::config::nuconfig::NuConfig;
use crate::config::nuconfig::NuConfig;
use std::fmt::Debug;
#[derive(PartialEq, Debug)]

View File

@ -1,4 +1,4 @@
use crate::data::config::{read, Conf, NuConfig};
use crate::config::{read, Conf, NuConfig};
use indexmap::IndexMap;
use nu_protocol::Value;
use nu_source::Tag;

View File

@ -1,7 +1,6 @@
use crate::prelude::*;
use derive_new::new;
use nu_protocol::{Dictionary, Primitive, UntaggedValue, Value};
use nu_source::{b, PrettyDebug, Spanned};
use nu_protocol::{Dictionary, MaybeOwned, Primitive, UntaggedValue, Value};
use nu_source::{b, DebugDocBuilder, PrettyDebug, Spanned, Tag};
#[derive(Debug, new)]
struct DebugEntry<'a> {

10
crates/nu-data/src/lib.rs Normal file
View File

@ -0,0 +1,10 @@
pub mod base;
pub mod command;
pub mod config;
pub mod dict;
pub mod primitive;
pub mod types;
pub mod utils;
pub mod value;
pub use dict::TaggedListBuilder;

View File

@ -1,4 +1,4 @@
use crate::data::{TaggedDictBuilder, Value};
use nu_data::{TaggedDictBuilder, Value};
use crate::prelude::*;
use itertools::join;
use sysinfo::ProcessExt;

View File

@ -1,8 +1,7 @@
use crate::prelude::*;
use log::trace;
use nu_errors::{CoerceInto, ShellError};
use nu_protocol::{Primitive, SpannedTypeName, UntaggedValue, Value};
use nu_source::Tagged;
use nu_source::{Tagged, TaggedItem};
pub trait ExtractType: Sized {
fn extract(value: &Value) -> Result<Self, ShellError>;

View File

@ -1,6 +1,6 @@
#![allow(clippy::type_complexity)]
use crate::data::value::compute_values;
use crate::value::compute_values;
use derive_new::new;
use nu_errors::ShellError;
use nu_protocol::hir::Operator;
@ -138,8 +138,7 @@ pub fn sort_columns(
if let Some(fmt) = format {
for k in values.iter() {
let k = k.clone().tagged_unknown();
let v =
crate::data::value::Date::naive_from_str(k.borrow_tagged())?.into_untagged_value();
let v = crate::value::Date::naive_from_str(k.borrow_tagged())?.into_untagged_value();
keys.push(fmt(&v, k.to_string())?);
}
} else {

View File

@ -3,10 +3,10 @@ pub mod split;
mod internal;
pub use crate::utils::data::group::group;
pub use crate::utils::data::split::split;
pub use crate::utils::group::group;
pub use crate::utils::split::split;
use crate::utils::data::internal::*;
use crate::utils::internal::*;
use derive_new::new;
use getset::Getters;
@ -97,9 +97,8 @@ pub fn report(
})
}
#[cfg(test)]
pub mod helpers {
use super::{report, Labels, Model, Operation, Range};
use super::Model;
use bigdecimal::BigDecimal;
use indexmap::indexmap;
use nu_errors::ShellError;
@ -132,8 +131,8 @@ pub mod helpers {
pub fn date(input: impl Into<String>) -> Value {
let key = input.into().tagged_unknown();
crate::data::value::Date::naive_from_str(key.borrow_tagged())
.unwrap()
crate::value::Date::naive_from_str(key.borrow_tagged())
.expect("date from string failed")
.into_untagged_value()
}
@ -201,12 +200,15 @@ pub mod helpers {
let grouper = Box::new(move |_, row: &Value| {
let key = String::from("date").tagged_unknown();
let group_key = row.get_data_by_key(key.borrow_spanned()).unwrap();
let group_key = row
.get_data_by_key(key.borrow_spanned())
.expect("get key failed");
group_key.format("%Y-%m-%d")
});
crate::utils::data::group(&sample, &Some(grouper), Tag::unknown()).unwrap()
crate::utils::group(&sample, &Some(grouper), Tag::unknown())
.expect("failed to create group")
}
pub fn date_formatter(
@ -215,13 +217,24 @@ pub mod helpers {
Box::new(move |date: &Value, _: String| date.format(&fmt))
}
fn assert_without_checking_percentages(report_a: Model, report_b: Model) {
pub fn assert_without_checking_percentages(report_a: Model, report_b: Model) {
assert_eq!(report_a.labels.x, report_b.labels.x);
assert_eq!(report_a.labels.y, report_b.labels.y);
assert_eq!(report_a.ranges, report_b.ranges);
assert_eq!(report_a.data, report_b.data);
}
}
#[cfg(test)]
mod tests {
use super::helpers::{
assert_without_checking_percentages, committers, date_formatter, decimal, int, table,
};
use super::{report, Labels, Model, Operation, Range};
use nu_errors::ShellError;
use nu_protocol::Value;
use nu_source::{Tag, TaggedItem};
use nu_value_ext::ValueExt;
#[test]
fn prepares_report_using_accumulating_value() {
let committers = table(&committers());

View File

@ -2,7 +2,7 @@ use nu_errors::ShellError;
use nu_protocol::{SpannedTypeName, TaggedDictBuilder, UntaggedValue, Value};
use nu_source::Tag;
use crate::utils::data::group;
use crate::utils::group;
#[allow(clippy::type_complexity)]
pub fn split(

View File

@ -1,6 +1,6 @@
use crate::data::base::coerce_compare;
use crate::data::base::shape::{Column, InlineShape};
use crate::data::primitive::style_primitive;
use crate::base::coerce_compare;
use crate::base::shape::{Column, InlineShape};
use crate::primitive::style_primitive;
use chrono::{DateTime, NaiveDate, Utc};
use nu_errors::ShellError;
use nu_protocol::hir::Operator;

View File

@ -10,7 +10,7 @@ version = "0.18.2"
doctest = false
[dependencies]
nu-cli = {path = "../nu-cli", version = "0.18.2"}
nu-data = {path = "../nu-data", version = "0.18.2"}
nu-errors = {path = "../nu-errors", version = "0.18.2"}
nu-plugin = {path = "../nu-plugin", version = "0.18.2"}
nu-protocol = {path = "../nu-protocol", version = "0.18.2"}

View File

@ -32,7 +32,7 @@ pub fn view_text_value(value: &Value) {
let highlight_range_to: u64 = 0;
let mut theme = "OneHalfDark".to_string();
if let Ok(config) = nu_cli::data::config::config(Tag::unknown()) {
if let Ok(config) = nu_data::config::config(Tag::unknown()) {
if let Some(batvars) = config.get("textview") {
for (idx, value) in batvars.row_entries() {
match idx.as_ref() {