nu_protocol/config/
history.rs

1use super::{config_update_string_enum, prelude::*};
2use crate::{self as nu_protocol, ConfigWarning};
3
4#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)]
5pub enum HistoryFileFormat {
6    /// Store history as an SQLite database with additional context
7    #[cfg(feature = "sqlite")]
8    Sqlite,
9    /// store history as a plain text file where every line is one command (without any context such as timestamps)
10    Plaintext,
11}
12
13impl HistoryFileFormat {
14    pub fn default_file_name(self) -> std::path::PathBuf {
15        match self {
16            HistoryFileFormat::Plaintext => "history.txt",
17            #[cfg(feature = "sqlite")]
18            HistoryFileFormat::Sqlite => "history.sqlite3",
19        }
20        .into()
21    }
22}
23
24impl FromStr for HistoryFileFormat {
25    type Err = &'static str;
26
27    fn from_str(s: &str) -> Result<Self, Self::Err> {
28        match s.to_ascii_lowercase().as_str() {
29            #[cfg(feature = "sqlite")]
30            "sqlite" => Ok(Self::Sqlite),
31            "plaintext" => Ok(Self::Plaintext),
32            #[cfg(feature = "sqlite")]
33            _ => Err("'sqlite' or 'plaintext'"),
34            #[cfg(not(feature = "sqlite"))]
35            _ => Err("'plaintext'"),
36        }
37    }
38}
39
40impl UpdateFromValue for HistoryFileFormat {
41    fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
42        config_update_string_enum(self, value, path, errors)
43    }
44}
45
46#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)]
47pub struct HistoryConfig {
48    pub max_size: i64,
49    pub sync_on_enter: bool,
50    pub file_format: HistoryFileFormat,
51    pub isolation: bool,
52}
53
54impl HistoryConfig {
55    pub fn file_path(&self) -> Option<std::path::PathBuf> {
56        nu_path::nu_config_dir().map(|mut history_path| {
57            history_path.push(self.file_format.default_file_name());
58            history_path.into()
59        })
60    }
61}
62
63impl Default for HistoryConfig {
64    fn default() -> Self {
65        Self {
66            max_size: 100_000,
67            sync_on_enter: true,
68            file_format: HistoryFileFormat::Plaintext,
69            isolation: false,
70        }
71    }
72}
73
74impl UpdateFromValue for HistoryConfig {
75    fn update<'a>(
76        &mut self,
77        value: &'a Value,
78        path: &mut ConfigPath<'a>,
79        errors: &mut ConfigErrors,
80    ) {
81        let Value::Record { val: record, .. } = value else {
82            errors.type_mismatch(path, Type::record(), value);
83            return;
84        };
85
86        // might not be correct if file format was changed away from sqlite rather than isolation,
87        // but this is an edge case and the span of the relevant value here should be close enough
88        let mut isolation_span = value.span();
89
90        for (col, val) in record.iter() {
91            let path = &mut path.push(col);
92            match col.as_str() {
93                "isolation" => {
94                    isolation_span = val.span();
95                    self.isolation.update(val, path, errors)
96                }
97                "sync_on_enter" => self.sync_on_enter.update(val, path, errors),
98                "max_size" => self.max_size.update(val, path, errors),
99                "file_format" => self.file_format.update(val, path, errors),
100                _ => errors.unknown_option(path, val),
101            }
102        }
103
104        // Listing all formats separately in case additional ones are added
105        match (self.isolation, self.file_format) {
106            (true, HistoryFileFormat::Plaintext) => {
107                errors.warn(ConfigWarning::IncompatibleOptions {
108                    label: "history isolation only compatible with SQLite format",
109                    span: isolation_span,
110                    help: r#"disable history isolation, or set $env.config.history.file_format = "sqlite""#,
111                });
112            }
113            #[cfg(feature = "sqlite")]
114            (true, HistoryFileFormat::Sqlite) => (),
115            (false, _) => (),
116        }
117    }
118}