Skip to main content

nu_protocol/config/
hooks.rs

1use super::prelude::*;
2use crate::engine::EnvName;
3use std::collections::HashMap;
4
5/// Definition of a parsed hook from the config object
6#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
7pub struct Hooks {
8    pub pre_prompt: Vec<Value>,
9    pub pre_execution: Vec<Value>,
10    pub env_change: HashMap<EnvName, Vec<Value>>,
11    pub display_output: Option<Value>,
12    pub command_not_found: Option<Value>,
13}
14
15impl Hooks {
16    pub fn new() -> Self {
17        Self {
18            pre_prompt: Vec::new(),
19            pre_execution: Vec::new(),
20            env_change: HashMap::new(),
21            display_output: Some(Value::string(
22                "if (term size).columns >= 100 { table -e } else { table }",
23                Span::unknown(),
24            )),
25            command_not_found: None,
26        }
27    }
28}
29
30impl Default for Hooks {
31    fn default() -> Self {
32        Self::new()
33    }
34}
35
36impl IntoValue for Hooks {
37    fn into_value(self, span: Span) -> Value {
38        let env_change = self
39            .env_change
40            .into_iter()
41            .map(|(key, value)| (key.into_string(), value.into_value(span)))
42            .collect::<crate::Record>();
43
44        record! {
45            "pre_prompt" => self.pre_prompt.into_value(span),
46            "pre_execution" => self.pre_execution.into_value(span),
47            "env_change" => env_change.into_value(span),
48            "display_output" => self.display_output.into_value(span),
49            "command_not_found" => self.command_not_found.into_value(span),
50        }
51        .into_value(span)
52    }
53}
54
55impl UpdateFromValue for Hooks {
56    fn update<'a>(
57        &mut self,
58        value: &'a Value,
59        path: &mut ConfigPath<'a>,
60        errors: &mut ConfigErrors,
61    ) {
62        let Value::Record { val: record, .. } = value else {
63            errors.type_mismatch(path, Type::record(), value);
64            return;
65        };
66
67        for (col, val) in record.iter() {
68            let path = &mut path.push(col);
69            match col.as_str() {
70                "pre_prompt" => {
71                    if let Ok(hooks) = val.as_list() {
72                        self.pre_prompt = hooks.into()
73                    } else {
74                        errors.type_mismatch(path, Type::list(Type::Any), val);
75                    }
76                }
77                "pre_execution" => {
78                    if let Ok(hooks) = val.as_list() {
79                        self.pre_execution = hooks.into()
80                    } else {
81                        errors.type_mismatch(path, Type::list(Type::Any), val);
82                    }
83                }
84                "env_change" => {
85                    if let Ok(record) = val.as_record() {
86                        self.env_change = record
87                            .iter()
88                            .map(|(key, val)| {
89                                let old = self
90                                    .env_change
91                                    .remove(&EnvName::from(key))
92                                    .unwrap_or_default();
93                                let new = if let Ok(hooks) = val.as_list() {
94                                    hooks.into()
95                                } else {
96                                    errors.type_mismatch(
97                                        &path.push(key),
98                                        Type::list(Type::Any),
99                                        val,
100                                    );
101                                    old
102                                };
103                                (key.as_str().into(), new)
104                            })
105                            .collect();
106                    } else {
107                        errors.type_mismatch(path, Type::record(), val);
108                    }
109                }
110                "display_output" => {
111                    self.display_output = if val.is_nothing() {
112                        None
113                    } else {
114                        Some(val.clone())
115                    }
116                }
117                "command_not_found" => {
118                    self.command_not_found = if val.is_nothing() {
119                        None
120                    } else {
121                        Some(val.clone())
122                    }
123                }
124                _ => errors.unknown_option(path, val),
125            }
126        }
127    }
128}