fm/app/
session.rs

1use std::fs::File;
2
3use serde::Serialize;
4use serde_yaml_ng::{from_reader, to_writer, Error as YamlError, Value as YamlValue};
5
6use crate::common::{tilde, SESSION_PATH};
7use crate::io::MIN_WIDTH_FOR_DUAL_PANE;
8use crate::log_info;
9
10/// Everything about the current session.
11/// We keep track of display settings (metadata, dual pane, second pane as preview).
12/// Display hidden files is read from args or set to false by default.
13/// Since it's specific to a tab, it's not stored here.
14///
15/// Reads its display values from a session file and updates them when modified.
16/// The file is stored at [`crate::common::SESSION_PATH`] which points to `~/.config/fm/session.yaml`.
17/// Unreachable or unreadable files are ignored.
18///
19/// Holds settings about display :
20/// - do we display one or two tabs ? Default to true.
21/// - do we display files metadata ? Default to true.
22/// - do we use to second pane to preview files ? Default to false.
23#[derive(Debug, Serialize)]
24pub struct Session {
25    /// do we display one or two tabs ?
26    dual: bool,
27    /// do we display all info or only the filenames ?
28    metadata: bool,
29    /// use the second pane to preview
30    preview: bool,
31    /// session filepath
32    #[serde(skip_serializing)]
33    filepath: String,
34}
35
36impl Default for Session {
37    fn default() -> Self {
38        Self {
39            dual: true,
40            metadata: true,
41            preview: false,
42            filepath: tilde(SESSION_PATH).to_string(),
43        }
44    }
45}
46
47impl Session {
48    /// Creates a new instance of `DisplaySettings`.
49    /// Tries to read them from the session file.
50    /// Use default value if the file can't be read.
51    pub fn new(width: u16) -> Self {
52        Self::default().update_from_config(width)
53    }
54
55    fn update_from_config(mut self, width: u16) -> Self {
56        let Ok(file) = File::open(&self.filepath) else {
57            log_info!("Couldn't open file {file}", file = self.filepath);
58            return self;
59        };
60        let Ok(yaml): Result<YamlValue, YamlError> = from_reader(file) else {
61            log_info!(
62                "Couldn't parse session from file {file}",
63                file = self.filepath
64            );
65            return self;
66        };
67        match yaml["dual"] {
68            YamlValue::Bool(value) => self.dual = Self::parse_dual_pane(value, width),
69            _ => self.dual = true,
70        }
71        match yaml["metadata"] {
72            YamlValue::Bool(value) => self.metadata = value,
73            _ => self.metadata = true,
74        }
75        match yaml["preview"] {
76            YamlValue::Bool(value) => self.preview = value,
77            _ => self.preview = false,
78        }
79        self
80    }
81
82    fn parse_dual_pane(session_bool: bool, width: u16) -> bool {
83        if !Self::display_wide_enough(width) {
84            return false;
85        }
86        session_bool
87    }
88
89    pub fn dual(&self) -> bool {
90        self.dual
91    }
92
93    pub fn metadata(&self) -> bool {
94        self.metadata
95    }
96
97    pub fn preview(&self) -> bool {
98        self.preview
99    }
100
101    /// True iff the terminal is wide enough to display two panes
102    pub fn display_wide_enough(width: u16) -> bool {
103        width >= MIN_WIDTH_FOR_DUAL_PANE
104    }
105
106    /// True if we display 2 tabs.
107    /// It requires two conditions:
108    /// 1. The display should be wide enough, bigger than [`crate::io::MIN_WIDTH_FOR_DUAL_PANE`].
109    /// 2. The `dual_tab` setting must be true.
110    pub fn use_dual_tab(&self, width: u16) -> bool {
111        self.dual && Self::display_wide_enough(width)
112    }
113
114    pub fn set_dual(&mut self, dual: bool) {
115        self.dual = dual;
116        self.update_yaml_file();
117    }
118
119    pub fn toggle_dual(&mut self) {
120        self.dual = !self.dual;
121        self.update_yaml_file();
122    }
123
124    pub fn toggle_metadata(&mut self) {
125        self.metadata = !self.metadata;
126        self.update_yaml_file();
127    }
128
129    pub fn set_preview(&mut self) {
130        self.preview = true;
131        self.update_yaml_file();
132    }
133
134    pub fn toggle_preview(&mut self) {
135        self.preview = !self.preview;
136        self.update_yaml_file();
137    }
138
139    /// Writes itself to the session file.
140    /// Does nothing if an error is encountered while creating or writing to the session file.
141    fn update_yaml_file(&self) {
142        let mut file = match File::create(&self.filepath) {
143            Ok(file) => file,
144            Err(error) => {
145                log_info!(
146                    "Couldn't create session {file}. Error: {error:?}",
147                    file = self.filepath
148                );
149                return;
150            }
151        };
152        match to_writer(&mut file, &self) {
153            Ok(()) => (),
154            Err(e) => log_info!(
155                "Couldn't write config to session {file}. Error: {e:?}",
156                file = self.filepath
157            ),
158        }
159    }
160}