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