vtcode_config/loader/
bootstrap.rs

1use std::fs;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Context, Result};
5
6use crate::defaults::ConfigDefaultsProvider;
7
8pub const DEFAULT_GITIGNORE_FILE_NAME: &str = ".vtcodegitignore";
9
10/// Determine where configuration and gitignore files should be created when
11/// bootstrapping a workspace.
12pub fn determine_bootstrap_targets(
13    workspace: &Path,
14    use_home_dir: bool,
15    config_file_name: &str,
16    defaults_provider: &dyn ConfigDefaultsProvider,
17) -> Result<(PathBuf, PathBuf)> {
18    if let (true, Some(home_config_path)) = (
19        use_home_dir,
20        select_home_config_path(defaults_provider, config_file_name),
21    ) {
22        let gitignore_path = gitignore_path_for(&home_config_path);
23        return Ok((home_config_path, gitignore_path));
24    }
25
26    let config_path = workspace.join(config_file_name);
27    let gitignore_path = workspace.join(DEFAULT_GITIGNORE_FILE_NAME);
28    Ok((config_path, gitignore_path))
29}
30
31/// Returns the preferred gitignore path for a given configuration file.
32pub fn gitignore_path_for(config_path: &Path) -> PathBuf {
33    config_path
34        .parent()
35        .map(|parent| parent.join(DEFAULT_GITIGNORE_FILE_NAME))
36        .unwrap_or_else(|| PathBuf::from(DEFAULT_GITIGNORE_FILE_NAME))
37}
38
39/// Ensures the parent directory for the provided path exists, creating it if
40/// necessary.
41pub fn ensure_parent_dir(path: &Path) -> Result<()> {
42    if let Some(parent) = path.parent() {
43        if parent.exists() {
44            return Ok(());
45        }
46
47        fs::create_dir_all(parent)
48            .with_context(|| format!("Failed to create directory: {}", parent.display()))?;
49    }
50
51    Ok(())
52}
53
54/// Selects the home directory configuration path from the defaults provider or
55/// falls back to the system home directory.
56pub fn select_home_config_path(
57    defaults_provider: &dyn ConfigDefaultsProvider,
58    config_file_name: &str,
59) -> Option<PathBuf> {
60    let home_paths = defaults_provider.home_config_paths(config_file_name);
61    home_paths
62        .into_iter()
63        .next()
64        .or_else(|| default_home_dir().map(|dir| dir.join(config_file_name)))
65}
66
67/// Attempts to resolve the current user's home directory using common
68/// environment variables and the `dirs` crate fallback.
69pub fn default_home_dir() -> Option<PathBuf> {
70    if let Ok(home) = std::env::var("HOME") {
71        return Some(PathBuf::from(home));
72    }
73
74    if let Ok(userprofile) = std::env::var("USERPROFILE") {
75        return Some(PathBuf::from(userprofile));
76    }
77
78    dirs::home_dir()
79}