mirror of
https://github.com/nushell/nushell
synced 2024-12-28 05:53:09 +00:00
Add missing flags to existing commands (#565)
* Add missing flags to existing commands * fmt
This commit is contained in:
parent
29c8b826d4
commit
b719f8d4eb
6 changed files with 462 additions and 88 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -1730,8 +1730,10 @@ dependencies = [
|
||||||
"titlecase",
|
"titlecase",
|
||||||
"toml",
|
"toml",
|
||||||
"trash",
|
"trash",
|
||||||
|
"umask",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"url",
|
"url",
|
||||||
|
"users",
|
||||||
"uuid",
|
"uuid",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
@ -3170,6 +3172,12 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "umask"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "982efbf70ec4d28f7862062c03dd1a4def601a5079e0faf1edc55f2ad0f6fe46"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uncased"
|
name = "uncased"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
|
@ -3239,6 +3247,16 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "users"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8-width"
|
name = "utf8-width"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
|
|
@ -68,6 +68,10 @@ sha2 = "0.10.0"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
num = { version = "0.4.0", optional = true }
|
num = { version = "0.4.0", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
umask = "1.0.0"
|
||||||
|
users = "0.11.0"
|
||||||
|
|
||||||
[dependencies.polars]
|
[dependencies.polars]
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
|
@ -23,6 +23,11 @@ impl Command for Do {
|
||||||
SyntaxShape::Block(Some(vec![])),
|
SyntaxShape::Block(Some(vec![])),
|
||||||
"the block to run",
|
"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")
|
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
@ -37,6 +42,8 @@ impl Command for Do {
|
||||||
let block: Value = call.req(engine_state, stack, 0)?;
|
let block: Value = call.req(engine_state, stack, 0)?;
|
||||||
let block_id = block.as_block()?;
|
let block_id = block.as_block()?;
|
||||||
|
|
||||||
|
let ignore_errors = call.has_flag("ignore-errors");
|
||||||
|
|
||||||
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
||||||
|
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
@ -81,6 +88,15 @@ impl Command for Do {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eval_block(engine_state, &mut stack, block, input)
|
let result = eval_block(engine_state, &mut stack, block, input);
|
||||||
|
|
||||||
|
if ignore_errors {
|
||||||
|
match result {
|
||||||
|
Ok(x) => Ok(x),
|
||||||
|
Err(_) => Ok(PipelineData::new(call.head)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,11 @@ impl Command for For {
|
||||||
SyntaxShape::Block(Some(vec![])),
|
SyntaxShape::Block(Some(vec![])),
|
||||||
"the block to run",
|
"the block to run",
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"numbered",
|
||||||
|
"returned a numbered item ($it.index and $it.item)",
|
||||||
|
Some('n'),
|
||||||
|
)
|
||||||
.creates_scope()
|
.creates_scope()
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
@ -60,6 +65,8 @@ impl Command for For {
|
||||||
.as_block()
|
.as_block()
|
||||||
.expect("internal error: expected block");
|
.expect("internal error: expected block");
|
||||||
|
|
||||||
|
let numbered = call.has_flag("numbered");
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let engine_state = engine_state.clone();
|
let engine_state = engine_state.clone();
|
||||||
let block = engine_state.get_block(block_id).clone();
|
let block = engine_state.get_block(block_id).clone();
|
||||||
|
@ -68,8 +75,26 @@ impl Command for For {
|
||||||
match values {
|
match values {
|
||||||
Value::List { vals, .. } => Ok(vals
|
Value::List { vals, .. } => Ok(vals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| {
|
.enumerate()
|
||||||
stack.add_var(var_id, x);
|
.map(move |(idx, x)| {
|
||||||
|
stack.add_var(
|
||||||
|
var_id,
|
||||||
|
if numbered {
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span: head,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span: head,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
//let block = engine_state.get_block(block_id);
|
//let block = engine_state.get_block(block_id);
|
||||||
match eval_block(&engine_state, &mut stack, &block, PipelineData::new(head)) {
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new(head)) {
|
||||||
|
@ -80,8 +105,26 @@ impl Command for For {
|
||||||
.into_pipeline_data(ctrlc)),
|
.into_pipeline_data(ctrlc)),
|
||||||
Value::Range { val, .. } => Ok(val
|
Value::Range { val, .. } => Ok(val
|
||||||
.into_range_iter()?
|
.into_range_iter()?
|
||||||
.map(move |x| {
|
.enumerate()
|
||||||
stack.add_var(var_id, x);
|
.map(move |(idx, x)| {
|
||||||
|
stack.add_var(
|
||||||
|
var_id,
|
||||||
|
if numbered {
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span: head,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span: head,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
//let block = engine_state.get_block(block_id);
|
//let block = engine_state.get_block(block_id);
|
||||||
match eval_block(&engine_state, &mut stack, &block, PipelineData::new(head)) {
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new(head)) {
|
||||||
|
|
|
@ -3,10 +3,14 @@ use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, DataSource, IntoInterruptiblePipelineData, PipelineData, PipelineMetadata, Signature,
|
Category, DataSource, IntoInterruptiblePipelineData, PipelineData, PipelineMetadata,
|
||||||
SyntaxShape, Value,
|
ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Ls;
|
pub struct Ls;
|
||||||
|
|
||||||
|
@ -27,6 +31,22 @@ impl Command for Ls {
|
||||||
SyntaxShape::GlobPattern,
|
SyntaxShape::GlobPattern,
|
||||||
"the glob pattern to use",
|
"the glob pattern to use",
|
||||||
)
|
)
|
||||||
|
.switch("all", "Show hidden files", Some('a'))
|
||||||
|
.switch(
|
||||||
|
"long",
|
||||||
|
"List all available columns for each entry",
|
||||||
|
Some('l'),
|
||||||
|
)
|
||||||
|
.switch(
|
||||||
|
"short-names",
|
||||||
|
"Only print the file names and not the path",
|
||||||
|
Some('s'),
|
||||||
|
)
|
||||||
|
.switch(
|
||||||
|
"du",
|
||||||
|
"Display the apparent directory size in place of the directory metadata size",
|
||||||
|
Some('d'),
|
||||||
|
)
|
||||||
.category(Category::FileSystem)
|
.category(Category::FileSystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +57,13 @@ impl Command for Ls {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let pattern = if let Some(expr) = call.positional.get(0) {
|
let all = call.has_flag("all");
|
||||||
|
let long = call.has_flag("long");
|
||||||
|
let short_names = call.has_flag("short-names");
|
||||||
|
|
||||||
|
let call_span = call.head;
|
||||||
|
|
||||||
|
let (pattern, arg_span) = if let Some(expr) = call.positional.get(0) {
|
||||||
let result = eval_expression(engine_state, stack, expr)?;
|
let result = eval_expression(engine_state, stack, expr)?;
|
||||||
let mut result = result.as_string()?;
|
let mut result = result.as_string()?;
|
||||||
|
|
||||||
|
@ -49,12 +75,11 @@ impl Command for Ls {
|
||||||
result.push('*');
|
result.push('*');
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
(result, expr.span)
|
||||||
} else {
|
} else {
|
||||||
"*".into()
|
("*".into(), call_span)
|
||||||
};
|
};
|
||||||
|
|
||||||
let call_span = call.head;
|
|
||||||
let glob = glob::glob(&pattern).map_err(|err| {
|
let glob = glob::glob(&pattern).map_err(|err| {
|
||||||
nu_protocol::ShellError::SpannedLabeledError(
|
nu_protocol::ShellError::SpannedLabeledError(
|
||||||
"Error extracting glob pattern".into(),
|
"Error extracting glob pattern".into(),
|
||||||
|
@ -63,67 +88,73 @@ impl Command for Ls {
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let hidden_dir_specified = is_hidden_dir(&pattern);
|
||||||
|
let mut hidden_dirs = vec![];
|
||||||
|
|
||||||
Ok(glob
|
Ok(glob
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| match x {
|
.filter_map(move |x| match x {
|
||||||
Ok(path) => match std::fs::symlink_metadata(&path) {
|
Ok(path) => {
|
||||||
Ok(metadata) => {
|
if permission_denied(&path) {
|
||||||
let is_symlink = metadata.file_type().is_symlink();
|
#[cfg(unix)]
|
||||||
let is_file = metadata.is_file();
|
let error_msg = format!(
|
||||||
let is_dir = metadata.is_dir();
|
"The permissions of {:o} do not allow access for this user",
|
||||||
let filesize = metadata.len();
|
path.metadata()
|
||||||
let mut cols = vec!["name".into(), "type".into(), "size".into()];
|
.expect(
|
||||||
|
"this shouldn't be called since we already know there is a dir"
|
||||||
let mut vals = vec![
|
)
|
||||||
Value::String {
|
.permissions()
|
||||||
val: path.to_string_lossy().to_string(),
|
.mode()
|
||||||
span: call_span,
|
& 0o0777
|
||||||
},
|
);
|
||||||
if is_symlink {
|
#[cfg(not(unix))]
|
||||||
Value::string("symlink", call_span)
|
let error_msg = String::from("Permission denied");
|
||||||
} else if is_file {
|
return Some(Value::Error {
|
||||||
Value::string("file", call_span)
|
error: ShellError::SpannedLabeledError(
|
||||||
} else if is_dir {
|
"Permission denied".into(),
|
||||||
Value::string("dir", call_span)
|
error_msg,
|
||||||
} else {
|
arg_span,
|
||||||
Value::Nothing { span: call_span }
|
),
|
||||||
},
|
});
|
||||||
Value::Filesize {
|
|
||||||
val: filesize as i64,
|
|
||||||
span: call_span,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if let Ok(date) = metadata.modified() {
|
|
||||||
let utc: DateTime<Utc> = date.into();
|
|
||||||
|
|
||||||
cols.push("modified".into());
|
|
||||||
vals.push(Value::Date {
|
|
||||||
val: utc.into(),
|
|
||||||
span: call_span,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Record {
|
|
||||||
cols,
|
|
||||||
vals,
|
|
||||||
span: call_span,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => Value::Record {
|
// if is_empty_dir(&p) {
|
||||||
cols: vec!["name".into(), "type".into(), "size".into()],
|
// return Ok(ActionStream::empty());
|
||||||
vals: vec![
|
// }
|
||||||
Value::String {
|
|
||||||
val: path.to_string_lossy().to_string(),
|
let metadata = match std::fs::symlink_metadata(&path) {
|
||||||
span: call_span,
|
Ok(metadata) => Some(metadata),
|
||||||
},
|
Err(e) => {
|
||||||
Value::Nothing { span: call_span },
|
if e.kind() == ErrorKind::PermissionDenied
|
||||||
Value::Nothing { span: call_span },
|
|| e.kind() == ErrorKind::Other
|
||||||
],
|
{
|
||||||
span: call_span,
|
None
|
||||||
},
|
} else {
|
||||||
},
|
return Some(Value::Error {
|
||||||
_ => Value::Nothing { span: call_span },
|
error: ShellError::IOError(format!("{}", e)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if path_contains_hidden_folder(&path, &hidden_dirs) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !all && !hidden_dir_specified && is_hidden_dir(&path) {
|
||||||
|
if path.is_dir() {
|
||||||
|
hidden_dirs.push(path);
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry =
|
||||||
|
dir_entry_dict(&path, metadata.as_ref(), call_span, long, short_names);
|
||||||
|
|
||||||
|
match entry {
|
||||||
|
Ok(value) => Some(value),
|
||||||
|
Err(err) => Some(Value::Error { error: err }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Some(Value::Nothing { span: call_span }),
|
||||||
})
|
})
|
||||||
.into_pipeline_data_with_metadata(
|
.into_pipeline_data_with_metadata(
|
||||||
PipelineMetadata {
|
PipelineMetadata {
|
||||||
|
@ -133,3 +164,270 @@ impl Command for Ls {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn permission_denied(dir: impl AsRef<Path>) -> bool {
|
||||||
|
match dir.as_ref().read_dir() {
|
||||||
|
Err(e) => matches!(e.kind(), std::io::ErrorKind::PermissionDenied),
|
||||||
|
Ok(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_hidden_dir(dir: impl AsRef<Path>) -> bool {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use std::os::windows::fs::MetadataExt;
|
||||||
|
|
||||||
|
if let Ok(metadata) = dir.as_ref().metadata() {
|
||||||
|
let attributes = metadata.file_attributes();
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||||
|
(attributes & 0x2) != 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
dir.as_ref()
|
||||||
|
.file_name()
|
||||||
|
.map(|name| name.to_string_lossy().starts_with('.'))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_contains_hidden_folder(path: &Path, folders: &[PathBuf]) -> bool {
|
||||||
|
let path_str = path.to_str().expect("failed to read path");
|
||||||
|
if folders
|
||||||
|
.iter()
|
||||||
|
.any(|p| path_str.starts_with(&p.to_str().expect("failed to read hidden paths")))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub 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>,
|
||||||
|
span: Span,
|
||||||
|
long: bool,
|
||||||
|
short_name: bool,
|
||||||
|
) -> Result<Value, ShellError> {
|
||||||
|
let mut cols = vec![];
|
||||||
|
let mut vals = vec![];
|
||||||
|
|
||||||
|
let name = if short_name {
|
||||||
|
filename.file_name().and_then(|s| s.to_str())
|
||||||
|
} else {
|
||||||
|
filename.to_str()
|
||||||
|
}
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ShellError::SpannedLabeledError(
|
||||||
|
format!("Invalid file name: {:}", filename.to_string_lossy()),
|
||||||
|
"invalid file name".into(),
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
cols.push("name".into());
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: name.to_string(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(md) = metadata {
|
||||||
|
cols.push("type".into());
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: get_file_type(md).to_string(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cols.push("type".into());
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
if long {
|
||||||
|
cols.push("target".into());
|
||||||
|
if let Some(md) = metadata {
|
||||||
|
if md.file_type().is_symlink() {
|
||||||
|
if let Ok(path_to_link) = filename.read_link() {
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: path_to_link.to_string_lossy().to_string(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: "Could not obtain target file's path".to_string(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if long {
|
||||||
|
if let Some(md) = metadata {
|
||||||
|
cols.push("readonly".into());
|
||||||
|
vals.push(Value::Bool {
|
||||||
|
val: md.permissions().readonly(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
let mode = md.permissions().mode();
|
||||||
|
cols.push("mode".into());
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: umask::Mode::from(mode).to_string(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
|
||||||
|
let nlinks = md.nlink();
|
||||||
|
cols.push("num_links".into());
|
||||||
|
vals.push(Value::Int {
|
||||||
|
val: nlinks as i64,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
|
||||||
|
let inode = md.ino();
|
||||||
|
cols.push("inode".into());
|
||||||
|
vals.push(Value::Int {
|
||||||
|
val: inode as i64,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
|
||||||
|
cols.push("uid".into());
|
||||||
|
if let Some(user) = users::get_user_by_uid(md.uid()) {
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: user.name().to_string_lossy().into(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vals.push(Value::nothing(span))
|
||||||
|
}
|
||||||
|
|
||||||
|
cols.push("group".into());
|
||||||
|
if let Some(group) = users::get_group_by_gid(md.gid()) {
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: group.name().to_string_lossy().into(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vals.push(Value::nothing(span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cols.push("size".to_string());
|
||||||
|
if let Some(md) = metadata {
|
||||||
|
if md.is_dir() {
|
||||||
|
let dir_size: u64 = md.len();
|
||||||
|
|
||||||
|
vals.push(Value::Filesize {
|
||||||
|
val: dir_size as i64,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else if md.is_file() {
|
||||||
|
vals.push(Value::Filesize {
|
||||||
|
val: md.len() as i64,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else if md.file_type().is_symlink() {
|
||||||
|
if let Ok(symlink_md) = filename.symlink_metadata() {
|
||||||
|
vals.push(Value::Filesize {
|
||||||
|
val: symlink_md.len() as i64,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(md) = metadata {
|
||||||
|
if long {
|
||||||
|
cols.push("created".to_string());
|
||||||
|
if let Ok(c) = md.created() {
|
||||||
|
let utc: DateTime<Utc> = c.into();
|
||||||
|
vals.push(Value::Date {
|
||||||
|
val: utc.into(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
cols.push("accessed".to_string());
|
||||||
|
if let Ok(a) = md.accessed() {
|
||||||
|
let utc: DateTime<Utc> = a.into();
|
||||||
|
vals.push(Value::Date {
|
||||||
|
val: utc.into(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cols.push("modified".to_string());
|
||||||
|
if let Ok(m) = md.modified() {
|
||||||
|
let utc: DateTime<Utc> = m.into();
|
||||||
|
vals.push(Value::Date {
|
||||||
|
val: utc.into(),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if long {
|
||||||
|
cols.push("created".to_string());
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
|
||||||
|
cols.push("accessed".to_string());
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
cols.push("modified".to_string());
|
||||||
|
vals.push(Value::nothing(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Record { cols, vals, span })
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use lscolors::{LsColors, Style};
|
use lscolors::{LsColors, Style};
|
||||||
use nu_color_config::{get_color_config, style_primitive};
|
use nu_color_config::{get_color_config, style_primitive};
|
||||||
use nu_engine::env_to_string;
|
use nu_engine::{env_to_string, CallExt};
|
||||||
use nu_protocol::ast::{Call, PathMember};
|
use nu_protocol::ast::{Call, PathMember};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Config, DataSource, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
Category, Config, DataSource, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||||
PipelineMetadata, ShellError, Signature, Span, Value, ValueStream,
|
PipelineMetadata, ShellError, Signature, Span, SyntaxShape, Value, ValueStream,
|
||||||
};
|
};
|
||||||
use nu_table::{StyledString, TextStyle, Theme};
|
use nu_table::{StyledString, TextStyle, Theme};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
@ -30,7 +30,14 @@ impl Command for Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("table").category(Category::Viewers)
|
Signature::build("table")
|
||||||
|
.named(
|
||||||
|
"start_number",
|
||||||
|
SyntaxShape::Int,
|
||||||
|
"row number to start viewing from",
|
||||||
|
Some('n'),
|
||||||
|
)
|
||||||
|
.category(Category::Viewers)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
|
@ -43,6 +50,8 @@ impl Command for Table {
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let config = stack.get_config().unwrap_or_default();
|
let config = stack.get_config().unwrap_or_default();
|
||||||
let color_hm = get_color_config(&config);
|
let color_hm = get_color_config(&config);
|
||||||
|
let start_num: Option<i64> = call.get_flag(engine_state, stack, "start_number")?;
|
||||||
|
let row_offset = start_num.unwrap_or_default() as usize;
|
||||||
|
|
||||||
let term_width = if let Some((Width(w), Height(_h))) = terminal_size::terminal_size() {
|
let term_width = if let Some((Width(w), Height(_h))) = terminal_size::terminal_size() {
|
||||||
(w - 1) as usize
|
(w - 1) as usize
|
||||||
|
@ -52,7 +61,7 @@ impl Command for Table {
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::Value(Value::List { vals, .. }, ..) => {
|
PipelineData::Value(Value::List { vals, .. }, ..) => {
|
||||||
let table = convert_to_table(0, &vals, ctrlc, &config, call.head)?;
|
let table = convert_to_table(row_offset, &vals, ctrlc, &config, call.head)?;
|
||||||
|
|
||||||
if let Some(table) = table {
|
if let Some(table) = table {
|
||||||
let result = nu_table::draw_table(&table, term_width, &color_hm, &config);
|
let result = nu_table::draw_table(&table, term_width, &color_hm, &config);
|
||||||
|
@ -153,27 +162,13 @@ impl Command for Table {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
Ok(PagingTableCreator {
|
Ok(PagingTableCreator {
|
||||||
row_offset: 0,
|
row_offset,
|
||||||
config,
|
config,
|
||||||
ctrlc: ctrlc.clone(),
|
ctrlc: ctrlc.clone(),
|
||||||
head,
|
head,
|
||||||
stream,
|
stream,
|
||||||
}
|
}
|
||||||
.into_pipeline_data(ctrlc))
|
.into_pipeline_data(ctrlc))
|
||||||
|
|
||||||
// let table = convert_to_table(stream, ctrlc, &config)?;
|
|
||||||
|
|
||||||
// if let Some(table) = table {
|
|
||||||
// let result = nu_table::draw_table(&table, term_width, &color_hm, &config);
|
|
||||||
|
|
||||||
// Ok(Value::String {
|
|
||||||
// val: result,
|
|
||||||
// span: call.head,
|
|
||||||
// }
|
|
||||||
// .into_pipeline_data())
|
|
||||||
// } else {
|
|
||||||
// Ok(PipelineData::new(call.head))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
|
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
Loading…
Reference in a new issue