Some more cleaning (#18186)

## Description

I've been experimenting with additional clippy lints and ran into these
while dealing with the warnings.

I wanted to separate these from the (eventual) PR for the lints since
these changes are a little more significant than the mostly stylistic
changes that come with the extra lints.
This commit is contained in:
Bahex authored and GitHub committed 2026-05-14 22:40:32 +03:00
1 parent cb757c110a
commit 0d4ae67698
20 files changed
+338 -417

No files matched your search

+9 -3
View File
@@ -4,7 +4,7 @@ id: let-if-let-some
language: rust
severity: hint
message: "This pattern can be confusing due to the repetition of `let` and `=`"
note: "It can be rewritten using `.map()` and `unwrap_or`/`unwrap_or_else` methods"
note: "It can be rewritten as a match expression or with `.map()` and `unwrap_or`/`unwrap_or_else` methods."
rule:
kind: let_declaration
@@ -17,7 +17,13 @@ rule:
# Multiple fix suggestions are supported after ast-grep 38.4
fix:
- title: Lazy
- title: Rewrite as a match expression
template: |
let $$$PAT = match $$$EXPR {
Some($VAR) => { $$$THEN }
None => { $$$ELSE }
};
- title: Rewrite with .map & .unwrap_or_else
template: |
let $$$PAT = $$$EXPR.map(|$VAR| {
$$$THEN
@@ -25,5 +31,5 @@ fix:
.unwrap_or_else(|| {
$$$ELSE
});
- title: Simple
- title: Rewrite with .map & .unwrap_or
template: "let $$$PAT = $$$EXPR.map(|$VAR| { $$$THEN }).unwrap_or($$$ELSE);"
@@ -382,14 +382,17 @@ impl<'a> Completer for CommandWideCompletion<'a> {
span: args_span,
} = get_command_arguments(working_set, self.expression);
let mut new_span = span;
// strip the placeholder
if self.strip
let new_span = if self.strip
&& let Some(last) = args.last_mut()
{
last.item.pop();
new_span = Span::new(span.start, span.end.saturating_sub(1));
}
if let Some(popped) = last.item.pop() {
last.span.end = last.span.end.saturating_sub(popped.len_utf8())
}
last.span
} else {
span
};
let mut block = working_set.get_block(self.block_id).clone();
+10 -10
View File
@@ -83,19 +83,19 @@ impl NuHelpCompleter {
let _ = write!(long_desc, " {}: {}\r\n", positional.name, positional.desc);
}
for positional in &sig.optional_positional {
let opt_suffix = if let Some(value) = &positional.default_value {
format!(
" (optional, default: {})",
&value.to_parsable_string(", ", &self.config),
)
} else {
(" (optional)").to_string()
};
let _ = write!(
long_desc,
" (optional) {}: {}{}\r\n",
positional.name, positional.desc, opt_suffix
" {}: {} (optional",
positional.name, positional.desc
);
if let Some(value) = &positional.default_value {
let _ = write!(
long_desc,
", default: {}",
&value.to_parsable_string(", ", &self.config),
);
}
let _ = write!(long_desc, ")\r\n");
}
if let Some(rest_positional) = &sig.rest_positional {
+29 -35
View File
@@ -54,45 +54,39 @@ fn get_prompt_string(
engine_state: &EngineState,
stack: &mut Stack,
) -> Option<String> {
stack
.get_env_var(engine_state, prompt)
.and_then(|v| match v {
Value::Closure { val, .. } => {
let result = ClosureEvalOnce::new(engine_state, stack, val.as_ref().clone())
.run_with_input(PipelineData::empty());
let mut output = match stack.get_env_var(engine_state, prompt)? {
Value::String { val, .. } => val.clone(),
Value::Closure { val, .. } => {
let result = ClosureEvalOnce::new(engine_state, stack, val.as_ref().clone())
.run_with_input(PipelineData::empty());
trace!(
"get_prompt_string (block) {}:{}:{}",
file!(),
line!(),
column!()
);
trace!(
"get_prompt_string (block) {}:{}:{}",
file!(),
line!(),
column!()
);
result
.map_err(|err| {
report_shell_error(None, engine_state, &err);
})
.ok()
}
Value::String { .. } => Some(PipelineData::value(v.clone(), None)),
_ => None,
})
.and_then(|pipeline_data| {
let output = pipeline_data.collect_string("", config).ok();
let ansi_output = output.map(|mut x| {
// Always reset the color at the start of the right prompt
// to ensure there is no ansi bleed over
if x.is_empty() && prompt == PROMPT_COMMAND_RIGHT {
x.insert_str(0, "\x1b[0m")
};
let result_string = result
.map_err(|err| report_shell_error(None, engine_state, &err))
.ok()
.and_then(|pd| pd.collect_string("", config).ok());
x
});
// Let's keep this for debugging purposes with nu --log-level warn
warn!("{}:{}:{} {:?}", file!(), line!(), column!(), ansi_output);
result_string?
}
_ => return None,
};
ansi_output
})
// Always reset the color at the start of the right prompt
// to ensure there is no ansi bleed over
if output.is_empty() && prompt == PROMPT_COMMAND_RIGHT {
output.insert_str(0, "\x1b[0m")
};
// Let's keep this for debugging purposes with nu --log-level warn
warn!("{}:{}:{} {:?}", file!(), line!(), column!(), output);
Some(output)
}
pub fn update_prompt(
+19 -14
View File
@@ -1,6 +1,7 @@
use nu_cmd_base::input_handler::{CmdArgument, operate};
use nu_engine::command_prelude::*;
use nu_protocol::FromValue;
use print_positions::print_positions;
#[derive(Clone)]
@@ -19,14 +20,28 @@ impl CmdArgument for Arguments {
}
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Default)]
enum FillAlignment {
#[default]
Left,
Right,
Middle,
MiddleRight,
}
impl FromValue for FillAlignment {
fn from_value(v: Value) -> Result<Self, ShellError> {
Ok(match String::from_value(v)?.to_ascii_lowercase().as_str() {
"l" | "left" => Self::Left,
"r" | "right" => Self::Right,
"c" | "center" | "m" | "middle" => Self::Middle,
"cr" | "centerright" | "mr" | "middleright" => Self::MiddleRight,
// TODO: This should probably be an error
_ => Self::Left,
})
}
}
impl Command for Fill {
fn name(&self) -> &str {
"fill"
@@ -152,23 +167,13 @@ fn fill(
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let width_arg: Option<usize> = call.get_flag(engine_state, stack, "width")?;
let alignment_arg: Option<String> = call.get_flag(engine_state, stack, "alignment")?;
let alignment = call
.get_flag::<FillAlignment>(engine_state, stack, "alignment")?
.unwrap_or_default();
let character_arg: Option<String> = call.get_flag(engine_state, stack, "character")?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let alignment = if let Some(arg) = alignment_arg {
match arg.to_ascii_lowercase().as_str() {
"l" | "left" => FillAlignment::Left,
"r" | "right" => FillAlignment::Right,
"c" | "center" | "m" | "middle" => FillAlignment::Middle,
"cr" | "centerright" | "mr" | "middleright" => FillAlignment::MiddleRight,
_ => FillAlignment::Left,
}
} else {
FillAlignment::Left
};
let width = width_arg.unwrap_or(1);
let character = character_arg.unwrap_or_else(|| " ".to_string());
+1 -11
View File
@@ -42,19 +42,9 @@ impl Command for DropColumn {
) -> Result<PipelineData, ShellError> {
let input = input.into_stream_or_original(engine_state);
// the number of columns to drop
let columns: Option<Spanned<i64>> = call.opt(engine_state, stack, 0)?;
let columns = call.opt::<usize>(engine_state, stack, 0)?.unwrap_or(1);
let from_left = call.has_flag(engine_state, stack, "left")?;
let columns = if let Some(columns) = columns {
if columns.item < 0 {
return Err(ShellError::NeedsPositiveValue { span: columns.span });
} else {
columns.item as usize
}
} else {
1
};
drop_cols(engine_state, input, call.head, columns, from_left)
}
+2 -12
View File
@@ -78,20 +78,10 @@ impl Command for Drop {
) -> Result<PipelineData, ShellError> {
let head = call.head;
let metadata = input.take_metadata();
let rows: Option<Spanned<i64>> = call.opt(engine_state, stack, 0)?;
let rows = call.opt::<usize>(engine_state, stack, 0)?.unwrap_or(1);
let mut values = input.into_iter_strict(head)?.collect::<Vec<_>>();
let rows_to_drop = if let Some(rows) = rows {
if rows.item < 0 {
return Err(ShellError::NeedsPositiveValue { span: rows.span });
} else {
rows.item as usize
}
} else {
1
};
values.truncate(values.len().saturating_sub(rows_to_drop));
values.truncate(values.len().saturating_sub(rows));
Ok(Value::list(values, head).into_pipeline_data_with_metadata(metadata))
}
}
+40 -46
View File
@@ -339,64 +339,62 @@ fn get_match_pattern_from_arguments(
let highlight_style =
style_computer.compute("search_result", &Value::string("search result", span));
let (regex_str, search_terms) = if let Some(regex) = regex {
if !terms.is_empty() {
let (regex_str, search_terms) = match (regex, terms.as_slice()) {
(Some(_), [_, ..]) => {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Cannot use a `--regex` parameter with additional search terms".into(),
span: call.get_flag_span(stack, "regex").expect("has flag"),
});
}
(Some(regex), []) => {
let flags = match (ignore_case, dotall) {
(false, false) => "",
(true, false) => "(?i)", // case insensitive
(false, true) => "(?s)", // allow . to match \n
(true, true) => "(?is)", // case insensitive and allow . to match \n
};
let flags = match (ignore_case, dotall) {
(false, false) => "",
(true, false) => "(?i)", // case insensitive
(false, true) => "(?s)", // allow . to match \n
(true, true) => "(?is)", // case insensitive and allow . to match \n
};
(flags.to_string() + regex.as_str(), Vec::new())
} else {
if dotall {
(flags.to_string() + regex.as_str(), Vec::new())
}
(None, _) if dotall => {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Flag --dotall only works for regex search".into(),
span: call.get_flag_span(stack, "dotall").expect("has flag"),
});
}
// NOTE: Should this be an error? It doesn't make sense to call `find` with no arguments.
// (None, []) => {}
(None, terms) => {
let mut regex = String::new();
let mut regex = String::new();
if ignore_case {
regex += "(?i)";
}
if ignore_case {
regex += "(?i)";
}
let search_terms = terms
.iter()
.map(|v| {
if ignore_case {
v.to_expanded_string("", &config).to_lowercase()
} else {
v.to_expanded_string("", &config)
}
})
.collect::<Vec<String>>();
let search_terms = terms
.iter()
.map(|v| {
if ignore_case {
v.to_expanded_string("", &config).to_lowercase()
} else {
v.to_expanded_string("", &config)
if let [first, rest @ ..] = search_terms.as_slice() {
regex.push_str(escape(first).as_ref());
for term in rest {
regex.push('|');
regex.push_str(escape(term).as_ref());
}
})
.collect::<Vec<String>>();
}
let escaped_terms = search_terms
.iter()
.map(|v| escape(v).into())
.collect::<Vec<String>>();
if let Some(term) = escaped_terms.first() {
regex += term;
(regex, search_terms)
}
for term in escaped_terms.iter().skip(1) {
regex += "|";
regex += term;
}
(regex, search_terms)
};
// TODO: Should be InvalidValue
let regex = Regex::new(regex_str.as_str()).map_err(|e| ShellError::TypeMismatch {
err_message: format!("invalid regex: {e}"),
span,
@@ -454,15 +452,11 @@ fn highlight_all_matches(pattern: &MatchPattern, text: &str) -> String {
let mut highlighted = String::new();
for cap in pattern.regex.captures_iter(text) {
let capture = match cap {
Ok(capture) => capture,
Err(_) => return pattern.string_style.paint(text).to_string(),
let Ok(capture) = cap else {
return pattern.string_style.paint(text).to_string();
};
let m = match capture.get(0) {
Some(m) => m,
None => continue,
};
let Some(m) = capture.get(0) else { continue };
highlighted.push_str(
&pattern
+2 -10
View File
@@ -89,7 +89,7 @@ fn first_helper(
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let rows: Option<Spanned<i64>> = call.opt(engine_state, stack, 0)?;
let rows: Option<usize> = call.opt(engine_state, stack, 0)?;
let strict_mode = call.has_flag(engine_state, stack, "strict")?;
// FIXME: for backwards compatibility reasons, if `rows` is not specified we
@@ -97,15 +97,7 @@ fn first_helper(
// remove `rows` so that `first` always returns a single element; getting a list of
// the first N elements is covered by `take`
let return_single_element = rows.is_none();
let rows = if let Some(rows) = rows {
if rows.item < 0 {
return Err(ShellError::NeedsPositiveValue { span: rows.span });
} else {
rows.item as usize
}
} else {
1
};
let rows = rows.unwrap_or(1);
let mut input = input;
let input_meta = input.take_metadata();
+28 -36
View File
@@ -80,7 +80,7 @@ fn extract_headers(
span: Span,
config: &Config,
) -> Result<(Vec<String>, Vec<String>), ShellError> {
table
let record = table
.first()
.ok_or_else(|| {
ShellError::Generic(GenericError::new(
@@ -88,46 +88,38 @@ fn extract_headers(
"unable to extract headers",
span,
))
})
.and_then(Value::as_record)
.and_then(|record| {
for v in record.values() {
if !is_valid_header(v) {
return Err(ShellError::TypeMismatch {
err_message: "needs compatible type: Null, String, Bool, Float, Int"
.to_string(),
span: v.span(),
});
}
}
})?
.as_record()?;
let old_headers = record.columns().cloned().collect();
let new_headers = record
.values()
.enumerate()
.map(|(idx, value)| {
let col = value.to_expanded_string("", config);
if col.is_empty() {
format!("column{idx}")
} else {
col
}
})
.collect();
let new_headers = record
.values()
.enumerate()
.map(|(idx, value)| make_header_string(config, idx, value))
.collect::<Result<Vec<_>, _>>()?;
Ok((old_headers, new_headers))
})
let old_headers = record.columns().cloned().collect();
Ok((old_headers, new_headers))
}
fn is_valid_header(value: &Value) -> bool {
matches!(
value,
fn make_header_string(config: &Config, idx: usize, value: &Value) -> Result<String, ShellError> {
match value {
Value::Nothing { .. }
| Value::String { val: _, .. }
| Value::Bool { val: _, .. }
| Value::Float { val: _, .. }
| Value::Int { val: _, .. }
)
| Value::String { .. }
| Value::Bool { .. }
| Value::Float { .. }
| Value::Int { .. } => {
let col = value.to_expanded_string("", config);
Ok(match col.is_empty() {
true => format!("column{idx}"),
false => col,
})
}
_ => Err(ShellError::TypeMismatch {
err_message: "needs compatible type: Null, String, Bool, Float, Int".to_string(),
span: value.span(),
}),
}
}
fn replace_headers(
+2 -10
View File
@@ -79,21 +79,13 @@ impl Command for Last {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let rows: Option<Spanned<i64>> = call.opt(engine_state, stack, 0)?;
let rows = call.opt::<usize>(engine_state, stack, 0)?;
let strict_mode = call.has_flag(engine_state, stack, "strict")?;
// FIXME: Please read the FIXME message in `first.rs`'s `first_helper` implementation.
// It has the same issue.
let return_single_element = rows.is_none();
let rows = if let Some(rows) = rows {
if rows.item < 0 {
return Err(ShellError::NeedsPositiveValue { span: rows.span });
} else {
rows.item as usize
}
} else {
1
};
let rows = rows.unwrap_or(1);
let mut input = input;
let metadata = input.take_metadata();
+14 -28
View File
@@ -83,37 +83,23 @@ impl Command for Window {
) -> Result<PipelineData, ShellError> {
let input = input.into_stream_or_original(engine_state);
let head = call.head;
let window_size: Value = call.req(engine_state, stack, 0)?;
let stride: Option<Value> = call.get_flag(engine_state, stack, "stride")?;
let remainder = call.has_flag(engine_state, stack, "remainder")?;
let size =
usize::try_from(window_size.as_int()?).map_err(|_| ShellError::NeedsPositiveValue {
span: window_size.span(),
})?;
let size = NonZeroUsize::try_from(size).map_err(|_| ShellError::IncorrectValue {
msg: "`window_size` cannot be zero".into(),
val_span: window_size.span(),
call_span: head,
})?;
let stride = if let Some(stride_val) = stride {
let stride = usize::try_from(stride_val.as_int()?).map_err(|_| {
ShellError::NeedsPositiveValue {
span: stride_val.span(),
}
})?;
NonZeroUsize::try_from(stride).map_err(|_| ShellError::IncorrectValue {
msg: "`stride` cannot be zero".into(),
val_span: stride_val.span(),
call_span: head,
})?
} else {
NonZeroUsize::MIN
let fix_call_span = |err: ShellError| match err {
ShellError::IncorrectValue { msg, val_span, .. } => ShellError::IncorrectValue {
msg,
val_span,
call_span: call.head,
},
_ => err,
};
let size: NonZeroUsize = call.req(engine_state, stack, 0).map_err(fix_call_span)?;
let stride: NonZeroUsize = call
.get_flag(engine_state, stack, "stride")
.map_err(fix_call_span)?
.unwrap_or(NonZeroUsize::MIN);
let remainder = call.has_flag(engine_state, stack, "remainder")?;
if remainder && size == stride {
super::chunks::chunks(engine_state, input, size, head)
} else if stride >= size {
+12 -8
View File
@@ -1258,14 +1258,18 @@ pub(crate) fn request_error_to_shell_error(span: Span, e: ShellErrorOrRequestErr
}
fn retrieve_http_proxy_from_env(engine_state: &EngineState, stack: &mut Stack) -> Option<String> {
stack
.get_env_var(engine_state, "http_proxy")
.or(stack.get_env_var(engine_state, "HTTP_PROXY"))
.or(stack.get_env_var(engine_state, "https_proxy"))
.or(stack.get_env_var(engine_state, "HTTPS_PROXY"))
.or(stack.get_env_var(engine_state, "ALL_PROXY"))
.cloned()
.and_then(|proxy| proxy.coerce_into_string().ok())
[
"http_proxy",
"HTTP_PROXY",
"https_proxy",
"HTTPS_PROXY",
"ALL_PROXY",
]
.iter()
.find_map(|name| stack.get_env_var(engine_state, name))?
.coerce_str()
.map(|s| s.into_owned())
.ok()
}
fn proxy_builder_from_env(
+84 -83
View File
@@ -1,4 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::FromValue;
use uuid::{Timestamp, Uuid};
#[derive(Clone)]
@@ -18,8 +19,8 @@ impl Command for RandomUuid {
.short('v')
.arg(SyntaxShape::Int)
.desc(
"The UUID version to generate (1, 3, 4, 5, 7). Defaults to 4 if not \
specified.",
"The UUID version to generate (1, 3, 4, 5, 7). \
Defaults to 4 if not specified.",
)
.completion(Completion::new_list(&["1", "3", "4", "5", "7"])),
)
@@ -28,8 +29,8 @@ impl Command for RandomUuid {
.short('n')
.arg(SyntaxShape::String)
.desc(
"The namespace for v3 and v5 UUIDs (dns, url, oid, x500). Required for v3 \
and v5.",
"The namespace for v3 and v5 UUIDs (dns, url, oid, x500). \
Required for v3 and v5.",
)
.completion(Completion::new_list(&["dns", "url", "oid", "x500"])),
)
@@ -63,7 +64,15 @@ impl Command for RandomUuid {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
uuid(engine_state, stack, call)
let fix_call_span = |err: ShellError| match err {
ShellError::IncorrectValue { msg, val_span, .. } => ShellError::IncorrectValue {
msg,
val_span,
call_span: call.head,
},
_ => err,
};
uuid(engine_state, stack, call).map_err(fix_call_span)
}
fn examples(&self) -> Vec<Example<'_>> {
@@ -117,7 +126,12 @@ fn uuid(
let uuid_str = match version {
1 => {
let ts = Timestamp::now(uuid::timestamp::context::NoContext);
let node_id = get_mac_address(engine_state, stack, call, span)?;
let MacAddr(node_id) = call
.get_flag::<MacAddr>(engine_state, stack, "mac")?
.ok_or_else(|| ShellError::MissingParameter {
param_name: "mac".to_string(),
span,
})?;
let uuid = Uuid::new_v1(ts, &node_id);
uuid.hyphenated().to_string()
}
@@ -235,50 +249,64 @@ fn validate_flags(
Ok(())
}
fn get_mac_address(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
span: Span,
) -> Result<[u8; 6], ShellError> {
let mac_str: Option<String> = call.get_flag(engine_state, stack, "mac")?;
struct MacAddr([u8; 6]);
let mac_str = match mac_str {
Some(mac) => mac,
None => {
return Err(ShellError::MissingParameter {
param_name: "mac".to_string(),
span,
});
}
};
impl FromValue for MacAddr {
fn from_value(v: Value) -> Result<Self, ShellError> {
let span = v.span();
let s = v.into_string()?;
let mac_parts = mac_str.split(':').collect::<Vec<&str>>();
if mac_parts.len() != 6 {
return Err(ShellError::IncorrectValue {
msg: "MAC address must be in the format XX:XX:XX:XX:XX:XX".to_string(),
val_span: span,
call_span: span,
let mut buf = [0_u8; 6];
let mut mac_parts = s.split(':').map(|x| u8::from_str_radix(x, 0x10));
let has_6_hexadecimal_parts = buf.iter_mut().all(|ele| match mac_parts.next() {
Some(Ok(n)) => {
*ele = n;
true
}
_ => false,
});
if has_6_hexadecimal_parts && mac_parts.next().is_none() {
Ok(Self(buf))
} else {
Err(ShellError::IncorrectValue {
msg: "MAC address must be in the format XX:XX:XX:XX:XX:XX".to_string(),
val_span: span,
call_span: span,
})
}
}
}
let mac: [u8; 6] = mac_parts
.iter()
.map(|x| u8::from_str_radix(x, 16))
.collect::<Result<Vec<u8>, _>>()
.map_err(|_| ShellError::IncorrectValue {
msg: "MAC address must be in the format XX:XX:XX:XX:XX:XX".to_string(),
val_span: span,
call_span: span,
})?
.try_into()
.map_err(|_| ShellError::IncorrectValue {
msg: "MAC address must be in the format XX:XX:XX:XX:XX:XX".to_string(),
val_span: span,
call_span: span,
})?;
struct NameSpace(Uuid);
Ok(mac)
impl FromValue for NameSpace {
fn from_value(v: Value) -> Result<Self, ShellError> {
let span = v.span();
let s = v.into_string()?;
let namespace = match s.to_lowercase().as_str() {
"dns" => Uuid::NAMESPACE_DNS,
"url" => Uuid::NAMESPACE_URL,
"oid" => Uuid::NAMESPACE_OID,
"x500" => Uuid::NAMESPACE_X500,
_ => match Uuid::parse_str(&s) {
Ok(uuid) => uuid,
Err(_) => {
return Err(ShellError::IncorrectValue {
msg:
"Namespace must be one of: dns, url, oid, x500, or a valid UUID string"
.to_string(),
val_span: span,
call_span: span,
});
}
},
};
Ok(Self(namespace))
}
}
fn get_namespace_and_name(
@@ -287,46 +315,19 @@ fn get_namespace_and_name(
call: &Call,
span: Span,
) -> Result<(Uuid, String), ShellError> {
let namespace_str: Option<String> = call.get_flag(engine_state, stack, "namespace")?;
let name: Option<String> = call.get_flag(engine_state, stack, "name")?;
let NameSpace(namespace) = call
.get_flag::<NameSpace>(engine_state, stack, "namespace")?
.ok_or_else(|| ShellError::MissingParameter {
param_name: "namespace".to_string(),
span,
})?;
let namespace_str = match namespace_str {
Some(ns) => ns,
None => {
return Err(ShellError::MissingParameter {
param_name: "namespace".to_string(),
span,
});
}
};
let name = match name {
Some(n) => n,
None => {
return Err(ShellError::MissingParameter {
param_name: "name".to_string(),
span,
});
}
};
let namespace = match namespace_str.to_lowercase().as_str() {
"dns" => Uuid::NAMESPACE_DNS,
"url" => Uuid::NAMESPACE_URL,
"oid" => Uuid::NAMESPACE_OID,
"x500" => Uuid::NAMESPACE_X500,
_ => match Uuid::parse_str(&namespace_str) {
Ok(uuid) => uuid,
Err(_) => {
return Err(ShellError::IncorrectValue {
msg: "Namespace must be one of: dns, url, oid, x500, or a valid UUID string"
.to_string(),
val_span: span,
call_span: span,
});
}
},
};
let name = call
.get_flag::<String>(engine_state, stack, "name")?
.ok_or_else(|| ShellError::MissingParameter {
param_name: "name".to_string(),
span,
})?;
Ok((namespace, name))
}
+30 -44
View File
@@ -100,28 +100,7 @@ impl Command for FormatDate {
) -> Result<PipelineData, ShellError> {
let list = call.has_flag(engine_state, stack, "list")?;
let format = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
// env var preference is documented at https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
// LC_ALL overrides LC_TIME, LC_TIME overrides LANG
// get the locale first so we can use the proper get_env_var functions since this is a const command
// we can override the locale by setting $env.NU_TEST_LOCALE_OVERRIDE or $env.LC_TIME
let locale = if let Some(loc) = stack
.get_env_var(engine_state, LOCALE_OVERRIDE_ENV_VAR)
.or_else(|| stack.get_env_var(engine_state, "LC_ALL"))
.or_else(|| stack.get_env_var(engine_state, "LC_TIME"))
.or_else(|| stack.get_env_var(engine_state, "LANG"))
{
let locale_str = loc.as_str()?.split('.').next().unwrap_or("en_US");
locale_str.try_into().unwrap_or(Locale::en_US)
} else {
get_system_locale_string()
.map(|l| l.replace('-', "_"))
.unwrap_or_else(|| String::from("en_US"))
.as_str()
.try_into()
.unwrap_or(Locale::en_US)
};
let locale = get_locale(|name| stack.get_env_var(engine_state, name))?;
run(engine_state, call, input, list, format, locale)
}
@@ -134,33 +113,40 @@ impl Command for FormatDate {
) -> Result<PipelineData, ShellError> {
let list = call.has_flag_const(working_set, "list")?;
let format = call.opt_const::<Spanned<String>>(working_set, 0)?;
// env var preference is documented at https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
// LC_ALL overrides LC_TIME, LC_TIME overrides LANG
// get the locale first so we can use the proper get_env_var functions since this is a const command
// we can override the locale by setting $env.NU_TEST_LOCALE_OVERRIDE or $env.LC_TIME
let locale = if let Some(loc) = working_set
.get_env_var(LOCALE_OVERRIDE_ENV_VAR)
.or_else(|| working_set.get_env_var("LC_ALL"))
.or_else(|| working_set.get_env_var("LC_TIME"))
.or_else(|| working_set.get_env_var("LANG"))
{
let locale_str = loc.as_str()?.split('.').next().unwrap_or("en_US");
locale_str.try_into().unwrap_or(Locale::en_US)
} else {
get_system_locale_string()
.map(|l| l.replace('-', "_"))
.unwrap_or_else(|| String::from("en_US"))
.as_str()
.try_into()
.unwrap_or(Locale::en_US)
};
let locale = get_locale(|name| working_set.get_env_var(name))?;
run(working_set.permanent(), call, input, list, format, locale)
}
}
fn get_locale<'a, F>(env_getter: F) -> Result<Locale, ShellError>
where
F: Fn(&str) -> Option<&'a Value> + 'a,
{
// env var preference is documented at https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
// LC_ALL overrides LC_TIME, LC_TIME overrides LANG
static LOCALE_VAR_NAMES: &[&str] = &[LOCALE_OVERRIDE_ENV_VAR, "LC_ALL", "LC_TIME", "LANG"];
// get the locale first so we can use the proper get_env_var functions since this is a const command
// we can override the locale by setting $env.NU_TEST_LOCALE_OVERRIDE or $env.LC_TIME
let locale_var = LOCALE_VAR_NAMES
.iter()
.find_map(|name| env_getter(name))
.map(Value::as_str)
.transpose()?;
let locale = match locale_var {
Some(loc) => (loc.split('.').next())
.and_then(|s| Locale::try_from(s).ok())
.unwrap_or(Locale::en_US),
None => get_system_locale_string()
.map(|l| l.replace('-', "_"))
.and_then(|s| Locale::try_from(s.as_str()).ok())
.unwrap_or(Locale::en_US),
};
Ok(locale)
}
fn run(
engine_state: &EngineState,
call: &Call,
+2 -2
View File
@@ -12,7 +12,7 @@ fn window_size_zero() -> Result {
let err = test().run("[0 1 2] | window 0").expect_shell_error()?;
match err {
ShellError::IncorrectValue { msg, .. } => {
assert_eq!(msg, "`window_size` cannot be zero");
assert_eq!(msg, "use a value other than 0");
Ok(())
}
err => Err(err.into()),
@@ -42,7 +42,7 @@ fn stride_zero() -> Result {
let err = test().run("[0 1 2] | window 1 -s 0").expect_shell_error()?;
match err {
ShellError::IncorrectValue { msg, .. } => {
assert_eq!(msg, "`stride` cannot be zero");
assert_eq!(msg, "use a value other than 0");
Ok(())
}
err => Err(err.into()),
+5 -9
View File
@@ -2097,9 +2097,7 @@ pub fn parse_range(working_set: &mut StateWorkingSet, span: Span) -> Option<Expr
let contents = working_set.get_span_contents(span);
let token = if let Ok(s) = String::from_utf8(contents.into()) {
s
} else {
let Ok(token) = String::from_utf8(contents.into()) else {
working_set.error(ParseError::NonUtf8(span));
return None;
};
@@ -2122,12 +2120,10 @@ pub fn parse_range(working_set: &mut StateWorkingSet, span: Span) -> Option<Expr
.filter_map(|(pos, _)| {
// paren_depth = count of unclosed parens prior to pos
let before = &token[..pos];
let paren_depth = before
.chars()
.filter(|&c| c == '(')
.count()
.checked_sub(before.chars().filter(|&c| c == ')').count());
paren_depth.and_then(|d| (d == 0).then_some(pos))
let paren_opened = before.chars().filter(|&c| c == '(').count();
let paren_closed = before.chars().filter(|&c| c == ')').count();
let paren_depth = paren_opened.checked_sub(paren_closed)?;
(paren_depth == 0).then_some(pos)
})
.collect();
+8 -12
View File
@@ -1,5 +1,5 @@
use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
collections::{BTreeMap, BTreeSet, HashMap, HashSet, hash_map::Entry},
sync::{
Arc, Mutex,
mpsc::{Receiver, RecvTimeoutError, Sender, TryRecvError},
@@ -84,7 +84,7 @@ impl Jobs {
pub fn add_job_with_id(&mut self, id: JobId, job: Job) -> Result<(), &'static str> {
self.assign_last_frozen_id_if_frozen(id, &job);
if let std::collections::hash_map::Entry::Vacant(e) = self.jobs.entry(id) {
if let Entry::Vacant(e) = self.jobs.entry(id) {
e.insert(job);
Ok(())
} else {
@@ -383,16 +383,12 @@ impl IgnoredMail {
fn pop_oldest(&mut self) -> Option<PipelineData> {
let (id, (tag, value)) = self.messages.pop_first()?;
if let Some(tag) = tag {
let needs_cleanup = if let Some(ids) = self.by_tag.get_mut(&tag) {
ids.remove(&id);
ids.is_empty()
} else {
false
};
if needs_cleanup {
self.by_tag.remove(&tag);
if let Some(tag) = tag
&& let Entry::Occupied(mut occupied_entry) = self.by_tag.entry(tag)
{
occupied_entry.get_mut().remove(&id);
if occupied_entry.get().is_empty() {
occupied_entry.remove();
}
}
+32 -38
View File
@@ -285,16 +285,17 @@ impl ChildProcess {
span: Span,
callback: Option<PostWaitCallback>,
) -> Result<Self, ShellError> {
let (stdout, stderr) = if let Some(combined) = reader {
(Some(combined), None)
} else {
let stdout = child.as_mut().stdout.take().map(convert_file);
let stderr = child.as_mut().stderr.take().map(convert_file);
let (stdout, stderr) = match reader {
Some(combined) => (Some(combined), None),
None => {
let stdout = child.as_mut().stdout.take().map(convert_file);
let stderr = child.as_mut().stderr.take().map(convert_file);
if swap {
(stderr, stdout)
} else {
(stdout, stderr)
if swap {
(stderr, stdout)
} else {
(stdout, stderr)
}
}
};
@@ -382,11 +383,11 @@ impl ChildProcess {
)));
}
let bytes = if let Some(stdout) = self.stdout {
collect_bytes(stdout).map_err(|err| IoError::new(err, self.span, None))?
} else {
Vec::new()
};
let bytes = (self.stdout)
.map(collect_bytes)
.transpose()
.map_err(|err| IoError::new(err, self.span, None))?
.unwrap_or_default();
let mut exit_status = self
.exit_status
@@ -462,39 +463,32 @@ impl ChildProcess {
pub fn wait_with_output(self) -> Result<ProcessOutput, ShellError> {
let from_io_error = IoError::factory(self.span, None);
let (stdout, stderr) = if let Some(stdout) = self.stdout {
let stderr = self
.stderr
.map(|stderr| thread::Builder::new().spawn(move || collect_bytes(stderr)))
.transpose()
.map_err(&from_io_error)?;
let stdout = collect_bytes(stdout).map_err(&from_io_error)?;
let (stdout, stderr) = match (self.stdout, self.stderr) {
(None, None) => (None, None),
(None, Some(stderr)) => (None, Some(collect_bytes(stderr).map_err(&from_io_error)?)),
(Some(stdout), None) => (Some(collect_bytes(stdout).map_err(&from_io_error)?), None),
(Some(stdout), Some(stderr)) => {
let stderr = thread::Builder::new()
.spawn(move || collect_bytes(stderr))
.map_err(&from_io_error)?;
let stderr = stderr
.map(|handle| {
handle.join().map_err(|e| match e.downcast::<io::Error>() {
let stdout = collect_bytes(stdout).map_err(&from_io_error)?;
let stderr = stderr
.join()
.map_err(|e| match e.downcast::<io::Error>() {
Ok(io) => from_io_error(*io).into(),
Err(err) => ShellError::Generic(GenericError::new(
"Unknown error",
format!("{err:?}"),
self.span,
)),
})
})
.transpose()?
.transpose()
.map_err(&from_io_error)?;
})?
.map_err(&from_io_error)?;
(Some(stdout), stderr)
} else {
let stderr = self
.stderr
.map(collect_bytes)
.transpose()
.map_err(&from_io_error)?;
(None, stderr)
(Some(stdout), Some(stderr))
}
};
let mut exit_status = self
+1 -1
View File
@@ -375,7 +375,7 @@ macro_rules! impl_from_value_for_uint {
let span = v.span();
const MAX: i64 = $max;
match v {
Value::Int { val, .. } | Value::Duration { val, .. } => {
Value::Int { val, .. } => {
match val {
i64::MIN..=-1 => Err(ShellError::NeedsPositiveValue { span }),
0..=MAX => Ok(val as $type),