Command especific configuration extraction baseline.

This commit is contained in:
Andrés N. Robalino
2020-08-26 10:05:07 -05:00
parent 034c33c2b5
commit 6685f74e03
8 changed files with 193 additions and 18 deletions

View File

@ -0,0 +1,246 @@
use crate::commands::table::options::{ConfigExtensions, NuConfig as TableConfiguration};
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
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};
use std::time::Instant;
const STREAM_PAGE_SIZE: usize = 1000;
const STREAM_TIMEOUT_CHECK_INTERVAL: usize = 100;
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"table"
}
fn signature(&self) -> Signature {
Signature::build("table").named(
"start_number",
SyntaxShape::Number,
"row number to start viewing from",
Some('n'),
)
}
fn usage(&self) -> &str {
"View the contents of the pipeline as a table."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
table(TableConfiguration::new(), args, registry).await
}
}
pub fn from_list(
values: &[Value],
configuration: &TableConfiguration,
starting_idx: usize,
) -> nu_table::Table {
let header_style = TextStyle {
is_bold: configuration.header_bold(),
alignment: configuration.header_alignment(),
color: configuration.header_color(),
};
let mut headers: Vec<StyledString> = nu_protocol::merge_descriptors(values)
.into_iter()
.map(|x| StyledString::new(x, header_style.clone()))
.collect();
let entries = values_to_entries(values, &mut headers, configuration, starting_idx);
nu_table::Table {
headers,
data: entries,
theme: configuration.table_mode(),
}
}
fn values_to_entries(
values: &[Value],
headers: &mut Vec<StyledString>,
configuration: &TableConfiguration,
starting_idx: usize,
) -> Vec<Vec<StyledString>> {
let disable_indexes = configuration.disabled_indexes();
let mut entries = vec![];
if headers.is_empty() {
headers.push(StyledString::new("".to_string(), TextStyle::basic()));
}
for (idx, value) in values.iter().enumerate() {
let mut row: Vec<StyledString> = headers
.iter()
.map(|d: &StyledString| {
if d.contents == "" {
match value {
Value {
value: UntaggedValue::Row(..),
..
} => StyledString::new(
format_leaf(&UntaggedValue::nothing()).plain_string(100_000),
style_leaf(&UntaggedValue::nothing()),
),
_ => StyledString::new(
format_leaf(value).plain_string(100_000),
style_leaf(value),
),
}
} else {
match value {
Value {
value: UntaggedValue::Row(..),
..
} => {
let data = value.get_data(&d.contents);
StyledString::new(
format_leaf(data.borrow()).plain_string(100_000),
style_leaf(data.borrow()),
)
}
_ => StyledString::new(
format_leaf(&UntaggedValue::nothing()).plain_string(100_000),
style_leaf(&UntaggedValue::nothing()),
),
}
}
})
.collect();
// Indices are green, bold, right-aligned:
if !disable_indexes {
row.insert(
0,
StyledString::new(
(starting_idx + idx).to_string(),
TextStyle {
alignment: Alignment::Right,
color: Some(ansi_term::Color::Green),
is_bold: true,
},
),
);
}
entries.push(row);
}
if !disable_indexes {
headers.insert(
0,
StyledString::new(
"#".to_owned(),
TextStyle {
alignment: Alignment::Center,
color: Some(ansi_term::Color::Green),
is_bold: true,
},
),
);
}
entries
}
async fn table(
configuration: TableConfiguration,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let mut args = args.evaluate_once(&registry).await?;
let mut finished = false;
// let host = args.host.clone();
let mut start_number = match args.get("start_number") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::Int(i)),
..
}) => {
if let Some(num) = i.to_usize() {
num
} else {
return Err(ShellError::labeled_error(
"Expected a row number",
"expected a row number",
&args.args.call_info.name_tag,
));
}
}
_ => 0,
};
let mut delay_slot = None;
let term_width = args.host.lock().width();
while !finished {
let mut new_input: VecDeque<Value> = VecDeque::new();
let start_time = Instant::now();
for idx in 0..STREAM_PAGE_SIZE {
if let Some(val) = delay_slot {
new_input.push_back(val);
delay_slot = None;
} else {
match args.input.next().await {
Some(a) => {
if !new_input.is_empty() {
if let Some(descs) = new_input.get(0) {
let descs = descs.data_descriptors();
let compare = a.data_descriptors();
if descs != compare {
delay_slot = Some(a);
break;
} else {
new_input.push_back(a);
}
} else {
new_input.push_back(a);
}
} else {
new_input.push_back(a);
}
}
_ => {
finished = true;
break;
}
}
// Check if we've gone over our buffering threshold
if (idx + 1) % STREAM_TIMEOUT_CHECK_INTERVAL == 0 {
let end_time = Instant::now();
// If we've been buffering over a second, go ahead and send out what we have so far
if (end_time - start_time).as_secs() >= 1 {
break;
}
}
}
}
let input: Vec<Value> = new_input.into();
if !input.is_empty() {
let t = from_list(&input, &configuration, start_number);
draw_table(&t, term_width);
}
start_number += input.len();
}
Ok(OutputStream::empty())
}

View File

@ -0,0 +1,4 @@
pub mod command;
mod options;
pub use command::Command as Table;

View File

@ -0,0 +1,112 @@
pub use nu_data::config::NuConfig;
use std::fmt::Debug;
pub trait ConfigExtensions: Debug + Send {
fn header_alignment(&self) -> nu_table::Alignment;
fn header_color(&self) -> Option<ansi_term::Color>;
fn header_bold(&self) -> bool;
fn table_mode(&self) -> nu_table::Theme;
fn disabled_indexes(&self) -> bool;
fn text_color(&self) -> Option<ansi_term::Color>;
fn line_color(&self) -> Option<ansi_term::Color>;
}
pub fn header_alignment(config: &NuConfig) -> nu_table::Alignment {
let vars = config.vars.lock();
let alignment = vars.get("header_align");
if alignment.is_none() {
return nu_table::Alignment::Center;
}
alignment.map_or(nu_table::Alignment::Left, |a| {
a.as_string().map_or(nu_table::Alignment::Center, |a| {
match a.to_lowercase().as_str() {
"center" | "c" => nu_table::Alignment::Center,
"right" | "r" => nu_table::Alignment::Right,
_ => nu_table::Alignment::Center,
}
})
})
}
pub fn get_color_for_config_key(config: &NuConfig, key: &str) -> Option<ansi_term::Color> {
let vars = config.vars.lock();
Some(match vars.get(key) {
Some(c) => match c.as_string() {
Ok(color) => match color.to_lowercase().as_str() {
"g" | "green" => ansi_term::Color::Green,
"r" | "red" => ansi_term::Color::Red,
"u" | "blue" => ansi_term::Color::Blue,
"b" | "black" => ansi_term::Color::Black,
"y" | "yellow" => ansi_term::Color::Yellow,
"p" | "purple" => ansi_term::Color::Purple,
"c" | "cyan" => ansi_term::Color::Cyan,
"w" | "white" => ansi_term::Color::White,
_ => ansi_term::Color::Green,
},
_ => ansi_term::Color::Green,
},
_ => ansi_term::Color::Green,
})
}
pub fn header_bold(config: &NuConfig) -> bool {
let vars = config.vars.lock();
vars.get("header_bold")
.map(|x| x.as_bool().unwrap_or(true))
.unwrap_or(true)
}
pub fn table_mode(config: &NuConfig) -> nu_table::Theme {
let vars = config.vars.lock();
vars.get("table_mode")
.map_or(nu_table::Theme::compact(), |mode| match mode.as_string() {
Ok(m) if m == "basic" => nu_table::Theme::basic(),
Ok(m) if m == "compact" => nu_table::Theme::compact(),
Ok(m) if m == "light" => nu_table::Theme::light(),
Ok(m) if m == "thin" => nu_table::Theme::thin(),
_ => nu_table::Theme::compact(),
})
}
pub fn disabled_indexes(config: &NuConfig) -> bool {
let vars = config.vars.lock();
vars.get("disable_table_indexes")
.map_or(false, |x| x.as_bool().unwrap_or(false))
}
impl ConfigExtensions for NuConfig {
fn header_alignment(&self) -> nu_table::Alignment {
header_alignment(self)
}
fn header_color(&self) -> Option<ansi_term::Color> {
get_color_for_config_key(self, "header_color")
}
fn text_color(&self) -> Option<ansi_term::Color> {
get_color_for_config_key(self, "text_color")
}
fn line_color(&self) -> Option<ansi_term::Color> {
get_color_for_config_key(self, "line_color")
}
fn header_bold(&self) -> bool {
header_bold(self)
}
fn table_mode(&self) -> nu_table::Theme {
table_mode(self)
}
fn disabled_indexes(&self) -> bool {
disabled_indexes(self)
}
}