1use 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("src/main.rs not found (run `tideway init` if this is a Tideway project)");
22 }
23
24 Ok(())
25}
26
27pub fn ensure_env(project_dir: &Path, fix_env: bool) -> Result<()> {
28 let env_path = project_dir.join(".env");
29 if env_path.exists() {
30 return Ok(());
31 }
32
33 let env_example_path = project_dir.join(".env.example");
34 if !env_example_path.exists() {
35 print_warning(".env not found and .env.example is missing");
36 return Ok(());
37 }
38
39 if fix_env {
40 fs::copy(&env_example_path, &env_path)
41 .with_context(|| format!("Failed to copy {}", env_example_path.display()))?;
42 print_success("Created .env from .env.example");
43 return Ok(());
44 }
45
46 print_warning("Missing .env (copy .env.example or run with --fix-env)");
47 Ok(())
48}
49
50pub fn read_env_map(path: &Path) -> Option<BTreeMap<String, String>> {
51 let contents = fs::read_to_string(path).ok()?;
52 Some(parse_env_map(&contents))
53}
54
55fn parse_env_map(contents: &str) -> BTreeMap<String, String> {
56 let mut vars = BTreeMap::new();
57 for line in contents.lines() {
58 let trimmed = line.trim();
59 if trimmed.is_empty() || trimmed.starts_with('#') {
60 continue;
61 }
62 if let Some((key, value)) = trimmed.split_once('=') {
63 let key = key.trim();
64 if !key.is_empty() {
65 let value = value.trim().trim_matches('"').trim_matches('\'');
66 vars.insert(key.to_string(), value.to_string());
67 }
68 }
69 }
70 vars
71}