Skip to main content

tideway_cli/
env.rs

1//! Shared environment helpers for CLI commands.
2
3use anyhow::{Context, Result};
4use std::collections::BTreeMap;
5use std::fs;
6use std::path::Path;
7
8use crate::{print_success, print_warning};
9
10pub fn ensure_project_dir(project_dir: &Path) -> Result<()> {
11    let cargo_toml = project_dir.join("Cargo.toml");
12    if !cargo_toml.exists() {
13        return Err(anyhow::anyhow!(
14            "Cargo.toml not found in {}",
15            project_dir.display()
16        ));
17    }
18
19    let main_rs = project_dir.join("src").join("main.rs");
20    if !main_rs.exists() {
21        print_warning(
22            "src/main.rs not found (advanced: run `tideway init` for existing projects; for greenfield apps use `tideway new <app>`)",
23        );
24    }
25
26    Ok(())
27}
28
29pub fn ensure_env(project_dir: &Path, fix_env: bool) -> Result<()> {
30    let env_path = project_dir.join(".env");
31    if env_path.exists() {
32        return Ok(());
33    }
34
35    let env_example_path = project_dir.join(".env.example");
36    if !env_example_path.exists() {
37        print_warning(".env not found and .env.example is missing");
38        return Ok(());
39    }
40
41    if fix_env {
42        fs::copy(&env_example_path, &env_path)
43            .with_context(|| format!("Failed to copy {}", env_example_path.display()))?;
44        print_success("Created .env from .env.example");
45        return Ok(());
46    }
47
48    print_warning("Missing .env (copy .env.example or run with --fix-env)");
49    Ok(())
50}
51
52pub fn read_env_map(path: &Path) -> Option<BTreeMap<String, String>> {
53    let contents = fs::read_to_string(path).ok()?;
54    Some(parse_env_map(&contents))
55}
56
57fn parse_env_map(contents: &str) -> BTreeMap<String, String> {
58    let mut vars = BTreeMap::new();
59    for line in contents.lines() {
60        let trimmed = line.trim();
61        if trimmed.is_empty() || trimmed.starts_with('#') {
62            continue;
63        }
64        if let Some((key, value)) = trimmed.split_once('=') {
65            let key = key.trim();
66            if !key.is_empty() {
67                let value = value.trim().trim_matches('"').trim_matches('\'');
68                vars.insert(key.to_string(), value.to_string());
69            }
70        }
71    }
72    vars
73}