use std::path::{Path, PathBuf};
use super::Rule;
pub(in crate::parser) fn reject_misplaced_dotenv_directive(
raw: &str,
span: pest::Span<'_>,
) -> Result<(), pest::error::Error<Rule>> {
if looks_like_dotenv_directive(raw) {
return Err(custom_error(
"dotenv directives must appear before the first ---".to_string(),
span,
));
}
Ok(())
}
pub(in crate::parser) fn resolve_dotenv_path(working_dir: &Path, raw_path: &str) -> PathBuf {
let path = PathBuf::from(raw_path);
let resolved = if path.is_absolute() {
path
} else {
working_dir.join(path)
};
normalize_path_lexically(resolved)
}
fn looks_like_dotenv_directive(raw: &str) -> bool {
let trimmed = strip_guard_prefix(raw.trim_start());
let Some(path) = trimmed.strip_prefix("dotenv") else {
return false;
};
let path = path.trim_start();
!path.is_empty() && !path.contains("{{") && !path.contains("[[")
}
fn strip_guard_prefix(raw: &str) -> &str {
if !raw.starts_with('[') {
return raw;
}
let mut in_single = false;
let mut in_double = false;
for (index, ch) in raw.char_indices().skip(1) {
match ch {
'\'' if !in_double => in_single = !in_single,
'"' if !in_single => in_double = !in_double,
']' if !in_single && !in_double => return raw[index + 1..].trim_start(),
_ => {}
}
}
raw
}
fn custom_error(message: String, span: pest::Span<'_>) -> pest::error::Error<Rule> {
pest::error::Error::new_from_span(
pest::error::ErrorVariant::CustomError { message },
span,
)
}
fn normalize_path_lexically(path: PathBuf) -> PathBuf {
let mut normalized = PathBuf::new();
let is_absolute = path.is_absolute();
for component in path.components() {
match component {
std::path::Component::Prefix(prefix) => normalized.push(prefix.as_os_str()),
std::path::Component::RootDir => normalized.push(component.as_os_str()),
std::path::Component::CurDir => {}
std::path::Component::ParentDir => {
if !normalized.pop() && !is_absolute {
normalized.push(component.as_os_str());
}
}
std::path::Component::Normal(part) => normalized.push(part),
}
}
if normalized.as_os_str().is_empty() {
PathBuf::from(".")
} else {
normalized
}
}