nextest_runner/user_config/
discovery.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Discovery of user config file location.
5
6use crate::errors::UserConfigError;
7use camino::Utf8PathBuf;
8use etcetera::{BaseStrategy, HomeDirError, base_strategy::Xdg};
9
10/// Returns candidate paths for the user config file, in order of priority.
11///
12/// On Unix/macOS, returns the XDG path:
13/// - `$XDG_CONFIG_HOME/nextest/config.toml`
14/// - `~/.config/nextest/config.toml` (fallback if XDG_CONFIG_HOME unset)
15///
16/// On Windows, returns two candidates in order:
17/// 1. Native path: `%APPDATA%\nextest\config.toml`
18/// 2. XDG path: `~/.config/nextest/config.toml` (for dotfiles portability)
19///
20/// The caller should check each path in order and use the first one that exists.
21pub fn user_config_paths() -> Result<Vec<Utf8PathBuf>, UserConfigError> {
22    let mut paths = Vec::new();
23
24    // On Windows, try native path first.
25    #[cfg(windows)]
26    if let Some(path) = native_config_path()? {
27        paths.push(path);
28    }
29
30    // Always include XDG path (primary on Unix/macOS, fallback on Windows).
31    if let Some(path) = xdg_config_path()? {
32        paths.push(path);
33    }
34
35    Ok(paths)
36}
37
38/// Returns the XDG config path.
39///
40/// Uses `Xdg` strategy explicitly to get `~/.config/nextest/config.toml` on all
41/// platforms. This is the primary path on Unix/macOS, and a fallback on Windows
42/// for users who manage dotfiles across platforms.
43fn xdg_config_path() -> Result<Option<Utf8PathBuf>, UserConfigError> {
44    let strategy = match Xdg::new() {
45        Ok(s) => s,
46        Err(HomeDirError) => return Ok(None),
47    };
48
49    let config_dir = strategy.config_dir().join("nextest");
50    let config_path = config_dir.join("config.toml");
51
52    Utf8PathBuf::try_from(config_path)
53        .map(Some)
54        .map_err(|error| UserConfigError::NonUtf8Path { error })
55}
56
57/// Returns the native Windows config path (%APPDATA%).
58#[cfg(windows)]
59fn native_config_path() -> Result<Option<Utf8PathBuf>, UserConfigError> {
60    use etcetera::base_strategy::Windows;
61
62    let strategy = match Windows::new() {
63        Ok(s) => s,
64        Err(HomeDirError) => return Ok(None),
65    };
66
67    let config_dir = strategy.config_dir().join("nextest");
68    let config_path = config_dir.join("config.toml");
69
70    Utf8PathBuf::try_from(config_path)
71        .map(Some)
72        .map_err(|error| UserConfigError::NonUtf8Path { error })
73}