Skip to main content

pinto/service/
settings.rs

1//! Read settings used by the TUI and human-readable CLI output.
2//!
3//! `Config` is crate-private, so [`TuiSettings`] exposes only the values needed by the binary-side
4//! TUI. Both CLI and TUI read the same `config.toml` through the shared persistence layer.
5
6use super::open_board;
7use crate::error::Result;
8use crate::timezone::DisplayTimezone;
9use std::path::Path;
10
11/// Settings required to launch the interactive Kanban view.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct TuiSettings {
14    /// Whether to display a confirmation popup upon exit.
15    pub confirm_quit: bool,
16    /// Workflow columns in their configured order.
17    pub workflow: Vec<String>,
18    /// Workflow columns hidden from the default Kanban display.
19    pub hidden_columns: Vec<String>,
20    /// Configured Kanban key assignments.
21    pub key_bindings: crate::kanban_keys::KeyBindings,
22    /// Render PBI bodies as Markdown in the details popup.
23    pub markdown: bool,
24    /// Timezone for human-readable timestamps in the details popup.
25    pub timezone: DisplayTimezone,
26}
27
28/// Read TUI settings from the board. Return [`crate::error::Error::NotInitialized`] when it is
29/// uninitialized.
30pub async fn tui_settings(project_dir: &Path) -> Result<TuiSettings> {
31    let (_board_dir, _repo, config) = open_board(project_dir).await?;
32    Ok(TuiSettings {
33        confirm_quit: config.tui.confirm_quit,
34        workflow: config.columns,
35        hidden_columns: config.tui.hidden_columns,
36        key_bindings: config.tui.key_bindings,
37        markdown: config.display.markdown,
38        timezone: config.display.timezone,
39    })
40}
41
42/// Display settings for the `show` command.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct DisplaySettings {
45    /// Render the PBI body (and common DoD) as Markdown instead of raw text.
46    pub markdown: bool,
47    /// Timezone for human-readable timestamps.
48    pub timezone: DisplayTimezone,
49}
50
51/// Read display settings from the board. Return [`crate::error::Error::NotInitialized`] when it is
52/// uninitialized.
53pub async fn display_settings(project_dir: &Path) -> Result<DisplaySettings> {
54    let (_board_dir, _repo, config) = open_board(project_dir).await?;
55    Ok(DisplaySettings {
56        markdown: config.display.markdown,
57        timezone: config.display.timezone,
58    })
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::service::init_board;
65    use tempfile::TempDir;
66
67    #[tokio::test]
68    async fn tui_settings_defaults_to_confirm_quit() {
69        let dir = TempDir::new().expect("temp dir");
70        init_board(dir.path()).await.expect("init");
71        let settings = tui_settings(dir.path()).await.expect("settings");
72        assert!(settings.confirm_quit);
73        assert_eq!(
74            settings
75                .key_bindings
76                .keys(crate::kanban_keys::KeyAction::Quit),
77            ["q".to_string(), "Esc".to_string()]
78        );
79        assert_eq!(settings.workflow, ["todo", "in-progress", "review", "done"]);
80        assert!(settings.hidden_columns.is_empty());
81    }
82
83    #[tokio::test]
84    async fn tui_settings_on_uninitialized_dir_errors() {
85        let dir = TempDir::new().expect("temp dir");
86        assert!(tui_settings(dir.path()).await.is_err());
87    }
88
89    #[tokio::test]
90    async fn tui_settings_defaults_to_markdown_on() {
91        let dir = TempDir::new().expect("temp dir");
92        init_board(dir.path()).await.expect("init");
93        let settings = tui_settings(dir.path()).await.expect("settings");
94        assert!(settings.markdown);
95    }
96
97    #[tokio::test]
98    async fn display_settings_defaults_to_markdown_on() {
99        let dir = TempDir::new().expect("temp dir");
100        init_board(dir.path()).await.expect("init");
101        let settings = display_settings(dir.path()).await.expect("settings");
102        assert!(settings.markdown);
103    }
104
105    #[tokio::test]
106    async fn display_settings_on_uninitialized_dir_errors() {
107        let dir = TempDir::new().expect("temp dir");
108        assert!(display_settings(dir.path()).await.is_err());
109    }
110}