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