nu_protocol/config/
helper.rs1use super::error::ConfigErrors;
2use crate::{Record, ShellError, Span, Type, Value};
3use std::{
4 borrow::Borrow,
5 collections::HashMap,
6 fmt::{self, Display},
7 hash::Hash,
8 ops::{Deref, DerefMut},
9 str::FromStr,
10};
11
12pub(super) struct ConfigPath<'a> {
13 components: Vec<&'a str>,
14}
15
16impl<'a> ConfigPath<'a> {
17 pub fn new() -> Self {
18 Self {
19 components: vec!["$env.config"],
20 }
21 }
22
23 pub fn push(&mut self, key: &'a str) -> ConfigPathScope<'_, 'a> {
24 self.components.push(key);
25 ConfigPathScope { inner: self }
26 }
27}
28
29impl Display for ConfigPath<'_> {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 write!(f, "{}", self.components.join("."))
32 }
33}
34
35pub(super) struct ConfigPathScope<'whole, 'part> {
36 inner: &'whole mut ConfigPath<'part>,
37}
38
39impl Drop for ConfigPathScope<'_, '_> {
40 fn drop(&mut self) {
41 self.inner.components.pop();
42 }
43}
44
45impl<'a> Deref for ConfigPathScope<'_, 'a> {
46 type Target = ConfigPath<'a>;
47
48 fn deref(&self) -> &Self::Target {
49 self.inner
50 }
51}
52
53impl DerefMut for ConfigPathScope<'_, '_> {
54 fn deref_mut(&mut self) -> &mut Self::Target {
55 self.inner
56 }
57}
58
59pub(super) trait UpdateFromValue: Sized {
60 fn update<'a>(
61 &mut self,
62 value: &'a Value,
63 path: &mut ConfigPath<'a>,
64 errors: &mut ConfigErrors,
65 );
66}
67
68impl UpdateFromValue for Value {
69 fn update(&mut self, value: &Value, _path: &mut ConfigPath, _errors: &mut ConfigErrors) {
70 *self = value.clone();
71 }
72}
73
74impl UpdateFromValue for bool {
75 fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
76 if let Ok(val) = value.as_bool() {
77 *self = val;
78 } else {
79 errors.type_mismatch(path, Type::Bool, value);
80 }
81 }
82}
83
84impl UpdateFromValue for i64 {
85 fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
86 if let Ok(val) = value.as_int() {
87 *self = val;
88 } else {
89 errors.type_mismatch(path, Type::Int, value);
90 }
91 }
92}
93
94impl UpdateFromValue for usize {
95 fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
96 if let Ok(val) = value.as_int() {
97 if let Ok(val) = val.try_into() {
98 *self = val;
99 } else {
100 errors.invalid_value(path, "a non-negative integer", value);
101 }
102 } else {
103 errors.type_mismatch(path, Type::Int, value);
104 }
105 }
106}
107
108impl UpdateFromValue for String {
109 fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
110 if let Ok(val) = value.as_str() {
111 *self = val.into();
112 } else {
113 errors.type_mismatch(path, Type::String, value);
114 }
115 }
116}
117
118impl<K, V> UpdateFromValue for HashMap<K, V>
119where
120 K: Borrow<str> + for<'a> From<&'a str> + Eq + Hash,
121 V: Default + UpdateFromValue,
122{
123 fn update<'a>(
124 &mut self,
125 value: &'a Value,
126 path: &mut ConfigPath<'a>,
127 errors: &mut ConfigErrors,
128 ) {
129 if let Ok(record) = value.as_record() {
130 *self = record
131 .iter()
132 .map(|(key, val)| {
133 let mut old = self.remove(key).unwrap_or_default();
134 old.update(val, &mut path.push(key), errors);
135 (key.as_str().into(), old)
136 })
137 .collect();
138 } else {
139 errors.type_mismatch(path, Type::record(), value);
140 }
141 }
142}
143
144pub(super) fn config_update_string_enum<T>(
145 choice: &mut T,
146 value: &Value,
147 path: &mut ConfigPath,
148 errors: &mut ConfigErrors,
149) where
150 T: FromStr,
151 T::Err: Display,
152{
153 if let Ok(str) = value.as_str() {
154 match str.parse() {
155 Ok(val) => *choice = val,
156 Err(err) => errors.invalid_value(path, err.to_string(), value),
157 }
158 } else {
159 errors.type_mismatch(path, Type::String, value);
160 }
161}
162
163pub fn extract_value<'record>(
164 column: &'static str,
165 record: &'record Record,
166 span: Span,
167) -> Result<&'record Value, ShellError> {
168 record
169 .get(column)
170 .ok_or_else(|| ShellError::MissingRequiredColumn { column, span })
171}