Skip to main content

bear_rs/
prefs.rs

1use std::path::PathBuf;
2
3use anyhow::Result;
4
5use crate::db::group_container_path;
6use crate::model::TagPosition;
7
8const PREFS_RELATIVE: &str = "Library/Preferences/9K33E3U3T4.net.shinyfrog.bear.plist";
9
10/// Bear user preferences relevant to the CLI.
11#[derive(Debug)]
12pub struct BearPrefs {
13    pub tag_position: TagPosition,
14    pub app_locking_enabled: bool,
15}
16
17impl Default for BearPrefs {
18    fn default() -> Self {
19        BearPrefs {
20            tag_position: TagPosition::Bottom,
21            app_locking_enabled: false,
22        }
23    }
24}
25
26/// Absolute path to the Bear preferences plist.
27pub fn prefs_path() -> Result<PathBuf> {
28    Ok(group_container_path()?.join(PREFS_RELATIVE))
29}
30
31/// Load Bear preferences from the shared group container plist.
32/// Falls back to defaults if the file is missing or a key is absent.
33pub fn load_prefs() -> Result<BearPrefs> {
34    let path = prefs_path()?;
35    if !path.exists() {
36        return Ok(BearPrefs::default());
37    }
38
39    let dict: plist::Dictionary = plist::from_file(&path)?;
40
41    let tag_position = match dict.get("SFGCTagPosition").and_then(|v| v.as_string()) {
42        Some("SFTagPositionTop") => TagPosition::Top,
43        _ => TagPosition::Bottom,
44    };
45
46    let app_locking_enabled = dict
47        .get("applicationLockingEnabled")
48        .and_then(|v| v.as_boolean())
49        .unwrap_or(false);
50
51    Ok(BearPrefs {
52        tag_position,
53        app_locking_enabled,
54    })
55}
56
57/// Check the app lock and bail with the native error message if enabled.
58pub fn check_app_lock() -> Result<()> {
59    if load_prefs()?.app_locking_enabled {
60        anyhow::bail!("Bear's app lock is enabled; disable it in Bear's settings to use the CLI.");
61    }
62    Ok(())
63}