Skip to main content

bw/platform/
appdata.rs

1//! CLI appdata directory resolution.
2//!
3//! Returns the directory that holds the legacy `config.json`, the session DB,
4//! and any other CLI state. The lookup order mirrors Node CLI's
5//! `service-container.ts` so users keep one appdata location across CLI
6//! versions:
7//!
8//! 1. A `bw-data` directory next to the running executable, if it exists (portable install).
9//! 2. `BITWARDENCLI_APPDATA_DIR` env var (if set and non-empty).
10//! 3. OS default:
11//!    - macOS: `$HOME/Library/Application Support/Bitwarden CLI`
12//!    - Windows: `%APPDATA%\Bitwarden CLI`
13//!    - Other: `$XDG_CONFIG_HOME/Bitwarden CLI` (falling back to `$HOME/.config/Bitwarden CLI`)
14
15use std::path::PathBuf;
16
17use color_eyre::eyre::{ContextCompat, Result};
18
19pub fn appdata_dir() -> Result<PathBuf> {
20    if let Some(portable) = portable_data_dir() {
21        return Ok(portable);
22    }
23    if let Some(v) = std::env::var_os("BITWARDENCLI_APPDATA_DIR")
24        && !v.is_empty()
25    {
26        return Ok(PathBuf::from(v));
27    }
28    default_appdata_dir()
29}
30
31fn portable_data_dir() -> Option<PathBuf> {
32    let exe = std::env::current_exe().ok()?;
33    let candidate = exe.parent()?.join("bw-data");
34    candidate.is_dir().then_some(candidate)
35}
36
37fn default_appdata_dir() -> Result<PathBuf> {
38    let parent = if cfg!(target_os = "windows") {
39        let appdata =
40            std::env::var_os("APPDATA").context("APPDATA environment variable is not set")?;
41        PathBuf::from(appdata)
42    } else if cfg!(target_os = "macos") {
43        let home = std::env::var_os("HOME").context("HOME environment variable is not set")?;
44        PathBuf::from(home)
45            .join("Library")
46            .join("Application Support")
47    } else {
48        match std::env::var_os("XDG_CONFIG_HOME") {
49            Some(v) if !v.is_empty() => PathBuf::from(v),
50            _ => {
51                let home =
52                    std::env::var_os("HOME").context("HOME environment variable is not set")?;
53                PathBuf::from(home).join(".config")
54            }
55        }
56    };
57    Ok(parent.join("Bitwarden CLI"))
58}