nu_data/config/
nuconfig.rs1use crate::config::{last_modified, read, Conf, Status};
2use indexmap::IndexMap;
3use nu_errors::ShellError;
4use nu_protocol::Value;
5use nu_source::Tag;
6use nu_test_support::NATIVE_PATH_ENV_VAR;
7use std::{fmt::Debug, path::PathBuf};
8
9use super::write;
10
11#[derive(Debug, Clone, Default)]
12pub struct NuConfig {
13 pub vars: IndexMap<String, Value>,
14 pub file_path: PathBuf,
15 pub modified_at: Status,
16}
17
18impl Conf for NuConfig {
19 fn is_modified(&self) -> Result<bool, Box<dyn std::error::Error>> {
20 self.is_modified()
21 }
22
23 fn var(&self, key: &str) -> Option<Value> {
24 self.var(key)
25 }
26
27 fn env(&self) -> Option<Value> {
28 self.env()
29 }
30
31 fn path(&self) -> Result<Option<Vec<PathBuf>>, ShellError> {
32 self.path()
33 }
34
35 fn reload(&mut self) {
36 if let Ok(variables) = read(Tag::unknown(), &Some(self.file_path.clone())) {
37 self.vars = variables;
38
39 self.modified_at = if let Ok(status) = last_modified(&Some(self.file_path.clone())) {
40 status
41 } else {
42 Status::Unavailable
43 };
44 }
45 }
46
47 fn clone_box(&self) -> Box<dyn Conf> {
48 Box::new(self.clone())
49 }
50}
51
52impl NuConfig {
53 pub fn load(cfg_file_path: Option<PathBuf>) -> Result<NuConfig, ShellError> {
54 let vars = read(Tag::unknown(), &cfg_file_path)?;
55 let modified_at = NuConfig::get_last_modified(&cfg_file_path);
56 let file_path = if let Some(file_path) = cfg_file_path {
57 file_path
58 } else {
59 crate::config::default_path()?
60 };
61
62 Ok(NuConfig {
63 vars,
64 file_path,
65 modified_at,
66 })
67 }
68
69 pub fn write(&self) -> Result<(), ShellError> {
71 write(&self.vars, &Some(self.file_path.clone()))
72 }
73
74 pub fn new() -> NuConfig {
75 let vars = if let Ok(variables) = read(Tag::unknown(), &None) {
76 variables
77 } else {
78 IndexMap::default()
79 };
80 let path = if let Ok(path) = crate::config::default_path() {
81 path
82 } else {
83 PathBuf::new()
84 };
85
86 NuConfig {
87 vars,
88 modified_at: NuConfig::get_last_modified(&None),
89 file_path: path,
90 }
91 }
92
93 pub fn get_last_modified(config_file: &Option<std::path::PathBuf>) -> Status {
94 if let Ok(status) = last_modified(config_file) {
95 status
96 } else {
97 Status::Unavailable
98 }
99 }
100
101 pub fn is_modified(&self) -> Result<bool, Box<dyn std::error::Error>> {
102 let modified_at = &self.modified_at;
103
104 Ok(match (NuConfig::get_last_modified(&None), modified_at) {
105 (Status::LastModified(left), Status::LastModified(right)) => {
106 let left = left.duration_since(std::time::UNIX_EPOCH)?;
107 let right = (*right).duration_since(std::time::UNIX_EPOCH)?;
108
109 left != right
110 }
111 (_, _) => false,
112 })
113 }
114
115 pub fn var(&self, key: &str) -> Option<Value> {
116 let vars = &self.vars;
117
118 if let Some(value) = vars.get(key) {
119 return Some(value.clone());
120 }
121
122 None
123 }
124
125 pub fn env_map(&self) -> IndexMap<String, String> {
127 let mut result = IndexMap::new();
128 if let Some(variables) = self.env() {
129 for var in variables.row_entries() {
130 if let Ok(value) = var.1.as_string() {
131 result.insert(var.0.clone(), value);
132 }
133 }
134 }
135 result
136 }
137
138 pub fn env(&self) -> Option<Value> {
139 let vars = &self.vars;
140
141 if let Some(env_vars) = vars.get("env") {
142 return Some(env_vars.clone());
143 }
144
145 None
146 }
147
148 pub fn path(&self) -> Result<Option<Vec<PathBuf>>, ShellError> {
149 let vars = &self.vars;
150
151 if let Some(path) = vars.get("path").or_else(|| vars.get(NATIVE_PATH_ENV_VAR)) {
152 path
153 .table_entries()
154 .map(|p| {
155 p.as_string().map(PathBuf::from).map_err(|_| {
156 ShellError::untagged_runtime_error("Could not format path entry as string!\nPath entry from config won't be added")
157 })
158 })
159 .collect::<Result<Vec<PathBuf>, ShellError>>().map(Some)
160 } else {
161 Ok(None)
162 }
163 }
164
165 fn load_scripts_if_present(&self, scripts_name: &str) -> Result<Vec<String>, ShellError> {
166 if let Some(array) = self.var(scripts_name) {
167 if !array.is_table() {
168 Err(ShellError::untagged_runtime_error(format!(
169 "expected an array of strings as {} commands",
170 scripts_name
171 )))
172 } else {
173 array.table_entries().map(Value::as_string).collect()
174 }
175 } else {
176 Ok(vec![])
177 }
178 }
179
180 pub fn exit_scripts(&self) -> Result<Vec<String>, ShellError> {
181 self.load_scripts_if_present("on_exit")
182 }
183
184 pub fn startup_scripts(&self) -> Result<Vec<String>, ShellError> {
185 self.load_scripts_if_present("startup")
186 }
187}