Skip to main content

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    #[inline]
90    pub fn dual(&self) -> bool {
91        self.dual
92    }
93
94    pub fn metadata(&self) -> bool {
95        self.metadata
96    }
97
98    pub fn preview(&self) -> bool {
99        self.preview
100    }
101
102    /// True iff the terminal is wide enough to display two panes
103    pub fn display_wide_enough(width: u16) -> bool {
104        width >= MIN_WIDTH_FOR_DUAL_PANE
105    }
106
107    /// True if we display 2 tabs.
108    /// It requires two conditions:
109    /// 1. The display should be wide enough, bigger than [`crate::io::MIN_WIDTH_FOR_DUAL_PANE`].
110    /// 2. The `dual_tab` setting must be true.
111    pub fn use_dual_tab(&self, width: u16) -> bool {
112        self.dual && Self::display_wide_enough(width)
113    }
114
115    pub fn set_dual(&mut self, dual: bool) {
116        self.dual = dual;
117        self.update_yaml_file();
118    }
119
120    pub fn toggle_dual(&mut self) {
121        self.dual = !self.dual;
122        self.update_yaml_file();
123    }
124
125    pub fn toggle_metadata(&mut self) {
126        self.metadata = !self.metadata;
127        self.update_yaml_file();
128    }
129
130    pub fn set_preview(&mut self) {
131        self.preview = true;
132        self.update_yaml_file();
133    }
134
135    pub fn toggle_preview(&mut self) {
136        self.preview = !self.preview;
137        self.update_yaml_file();
138    }
139
140    /// Writes itself to the session file.
141    /// Does nothing if an error is encountered while creating or writing to the session file.
142    fn update_yaml_file(&self) {
143        let mut file = match File::create(&self.filepath) {
144            Ok(file) => file,
145            Err(error) => {
146                log_info!(
147                    "Couldn't create session {file}. Error: {error:?}",
148                    file = self.filepath
149                );
150                return;
151            }
152        };
153        match to_writer(&mut file, &self) {
154            Ok(()) => (),
155            Err(e) => log_info!(
156                "Couldn't write config to session {file}. Error: {e:?}",
157                file = self.filepath
158            ),
159        }
160    }
161}