dotlock-bin 0.1.1

Encrypted project-local environment variables manager
use std::path::Path;

use crate::{
    domain::{error::DotLockError, model::DotLockResult},
    storage::secure_fs,
};

pub struct EnvEntry {
    pub key: String,
    pub value: String,
}

pub fn parse_env_file(path: &Path) -> DotLockResult<Vec<EnvEntry>> {
    let content = secure_fs::read_to_string(path)?;
    parse_env_str(path, &content)
}

fn parse_env_str(path: &Path, content: &str) -> DotLockResult<Vec<EnvEntry>> {
    let display = path.display().to_string();
    let mut entries = Vec::new();

    for (idx, raw) in content.lines().enumerate() {
        let line_num = idx + 1;
        let trimmed = raw.trim_start();

        if trimmed.is_empty() || trimmed.starts_with('#') {
            continue;
        }

        let after_export = trimmed
            .strip_prefix("export ")
            .unwrap_or(trimmed)
            .trim_start();

        let (key_part, value_part) = match after_export.split_once('=') {
            Some(parts) => parts,
            None => {
                return Err(DotLockError::EnvParseError {
                    path: display,
                    line: line_num,
                    message: "expected `KEY=VALUE`".to_string(),
                });
            }
        };

        let key = key_part.trim().to_string();
        if key.is_empty() {
            return Err(DotLockError::EnvParseError {
                path: display,
                line: line_num,
                message: "empty key".to_string(),
            });
        }

        let value = strip_quotes(value_part);
        entries.push(EnvEntry { key, value });
    }

    Ok(entries)
}

fn strip_quotes(raw: &str) -> String {
    let trimmed = raw.trim();
    if trimmed.len() >= 2 {
        let bytes = trimmed.as_bytes();
        let first = bytes[0];
        let last = bytes[bytes.len() - 1];
        if (first == b'"' && last == b'"') || (first == b'\'' && last == b'\'') {
            return trimmed[1..trimmed.len() - 1].to_string();
        }
    }
    trimmed.to_string()
}