mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Treating environment variables as Values (#497)
* Proof of concept treating env vars as Values * Refactor env var collection and method name * Remove unnecessary pub * Move env translations into a new file * Fix LS_COLORS to support any Value * Fix spans during env var translation * Add span to env var in cd * Improve error diagnostics * Fix non-string env vars failing string conversion * Make PROMPT_COMMAND a Block instead of String * Record host env vars to a fake file This will give spans to env vars that would otherwise be without one. Makes errors less confusing. * Add 'env' command to list env vars It will list also their values translated to strings * Sort env command by name; Add env var type * Remove obsolete test
This commit is contained in:
@ -1,9 +1,50 @@
|
||||
use crate::{ShellError, Value};
|
||||
use crate::{BlockId, ShellError, Span, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
const ANIMATE_PROMPT_DEFAULT: bool = false;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct EnvConversion {
|
||||
pub from_string: (BlockId, Span),
|
||||
pub to_string: (BlockId, Span),
|
||||
}
|
||||
|
||||
impl EnvConversion {
|
||||
pub fn from_record(value: &Value) -> Result<Self, ShellError> {
|
||||
let record = value.as_record()?;
|
||||
|
||||
let mut conv_map = HashMap::new();
|
||||
|
||||
for (k, v) in record.0.iter().zip(record.1) {
|
||||
if (k == "from_string") || (k == "to_string") {
|
||||
conv_map.insert(k.as_str(), (v.as_block()?, v.span()?));
|
||||
} else {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
"'from_string' and 'to_string' fields".into(),
|
||||
k.into(),
|
||||
value.span()?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
match (conv_map.get("from_string"), conv_map.get("to_string")) {
|
||||
(None, _) => Err(ShellError::MissingConfigValue(
|
||||
"'from_string' field".into(),
|
||||
value.span()?,
|
||||
)),
|
||||
(_, None) => Err(ShellError::MissingConfigValue(
|
||||
"'to_string' field".into(),
|
||||
value.span()?,
|
||||
)),
|
||||
(Some(from), Some(to)) => Ok(EnvConversion {
|
||||
from_string: *from,
|
||||
to_string: *to,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub filesize_metric: bool,
|
||||
@ -16,6 +57,7 @@ pub struct Config {
|
||||
pub float_precision: i64,
|
||||
pub filesize_format: String,
|
||||
pub use_ansi_coloring: bool,
|
||||
pub env_conversions: HashMap<String, EnvConversion>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -31,6 +73,7 @@ impl Default for Config {
|
||||
float_precision: 4,
|
||||
filesize_format: "auto".into(),
|
||||
use_ansi_coloring: true,
|
||||
env_conversions: HashMap::new(), // TODO: Add default conversoins
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,6 +172,16 @@ impl Value {
|
||||
"filesize_format" => {
|
||||
config.filesize_format = value.as_string()?.to_lowercase();
|
||||
}
|
||||
"env_conversions" => {
|
||||
let (env_vars, conversions) = value.as_record()?;
|
||||
let mut env_conversions = HashMap::new();
|
||||
|
||||
for (env_var, record) in env_vars.iter().zip(conversions) {
|
||||
env_conversions.insert(env_var.into(), EnvConversion::from_record(record)?);
|
||||
}
|
||||
|
||||
config.env_conversions = env_conversions;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -489,8 +489,7 @@ impl EngineState {
|
||||
"<unknown>".into()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn add_file(&mut self, filename: String, contents: Vec<u8>) -> usize {
|
||||
pub fn add_file(&mut self, filename: String, contents: Vec<u8>) -> usize {
|
||||
let next_span_start = self.next_span_start();
|
||||
let next_span_end = next_span_start + contents.len();
|
||||
|
||||
|
@ -24,7 +24,7 @@ pub struct Stack {
|
||||
/// Variables
|
||||
pub vars: HashMap<VarId, Value>,
|
||||
/// Environment variables arranged as a stack to be able to recover values from parent scopes
|
||||
pub env_vars: Vec<HashMap<String, String>>,
|
||||
pub env_vars: Vec<HashMap<String, Value>>,
|
||||
}
|
||||
|
||||
impl Default for Stack {
|
||||
@ -53,7 +53,7 @@ impl Stack {
|
||||
self.vars.insert(var_id, value);
|
||||
}
|
||||
|
||||
pub fn add_env_var(&mut self, var: String, value: String) {
|
||||
pub fn add_env_var(&mut self, var: String, value: Value) {
|
||||
if let Some(scope) = self.env_vars.last_mut() {
|
||||
scope.insert(var, value);
|
||||
} else {
|
||||
@ -85,7 +85,7 @@ impl Stack {
|
||||
}
|
||||
|
||||
/// Flatten the env var scope frames into one frame
|
||||
pub fn get_env_vars(&self) -> HashMap<String, String> {
|
||||
pub fn get_env_vars(&self) -> HashMap<String, Value> {
|
||||
let mut result = HashMap::new();
|
||||
|
||||
for scope in &self.env_vars {
|
||||
@ -95,17 +95,17 @@ impl Stack {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn get_env_var(&self, name: &str) -> Option<String> {
|
||||
pub fn get_env_var(&self, name: &str) -> Option<Value> {
|
||||
for scope in self.env_vars.iter().rev() {
|
||||
if let Some(v) = scope.get(name) {
|
||||
return Some(v.to_string());
|
||||
return Some(v.clone());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn remove_env_var(&mut self, name: &str) -> Option<String> {
|
||||
pub fn remove_env_var(&mut self, name: &str) -> Option<Value> {
|
||||
for scope in self.env_vars.iter_mut().rev() {
|
||||
if let Some(v) = scope.remove(name) {
|
||||
return Some(v);
|
||||
@ -135,7 +135,7 @@ impl Stack {
|
||||
for (i, scope) in self.env_vars.iter().rev().enumerate() {
|
||||
println!("env vars, scope {} (from the last);", i);
|
||||
for (var, val) in scope {
|
||||
println!(" {}: {:?}", var, val);
|
||||
println!(" {}: {:?}", var, val.clone().debug_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,10 +99,9 @@ pub enum ShellError {
|
||||
#[diagnostic(code(nu::shell::variable_not_found), url(docsrs))]
|
||||
EnvVarNotFoundAtRuntime(#[label = "environment variable not found"] Span),
|
||||
|
||||
#[error("Environment variable is not a string")]
|
||||
#[diagnostic(code(nu::shell::variable_not_found), url(docsrs))]
|
||||
EnvVarNotAString(#[label = "does not evaluate to a string"] Span),
|
||||
|
||||
// #[error("Environment variable is not a string")]
|
||||
// #[diagnostic(code(nu::shell::variable_not_found), url(docsrs))]
|
||||
// EnvVarNotAString(#[label = "does not evaluate to a string"] Span),
|
||||
#[error("Not found.")]
|
||||
#[diagnostic(code(nu::parser::not_found), url(docsrs))]
|
||||
NotFound(#[label = "did not find anything under this name"] Span),
|
||||
@ -235,6 +234,14 @@ pub enum ShellError {
|
||||
#[diagnostic(code(nu::shell::downcast_not_possible), url(docsrs))]
|
||||
DowncastNotPossible(String, #[label("{0}")] Span),
|
||||
|
||||
#[error("Unsupported config value")]
|
||||
#[diagnostic(code(nu::shell::unsupported_config_value), url(docsrs))]
|
||||
UnsupportedConfigValue(String, String, #[label = "expected {0}, got {1}"] Span),
|
||||
|
||||
#[error("Missing config value")]
|
||||
#[diagnostic(code(nu::shell::missing_config_value), url(docsrs))]
|
||||
MissingConfigValue(String, #[label = "missing {0}"] Span),
|
||||
|
||||
#[error("{0}")]
|
||||
#[diagnostic()]
|
||||
SpannedLabeledError(String, String, #[label("{1}")] Span),
|
||||
|
Reference in New Issue
Block a user