use std::path::{Component, Path, PathBuf};
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
use alp_core::{
ProjectContext, ProjectResolutionInput, ProjectSettings, format_iso8601_utc,
resolve_project_context,
};
use crate::cli::GlobalArgs;
pub fn generated_at_iso() -> String {
if let Ok(raw) = std::env::var("SOURCE_DATE_EPOCH") {
if let Ok(secs) = raw.trim().parse::<i64>() {
return format_iso8601_utc(secs, 0);
}
}
match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(d) => format_iso8601_utc(d.as_secs() as i64, d.subsec_millis()),
Err(_) => format_iso8601_utc(0, 0),
}
}
pub fn command_on_path(command: &str) -> bool {
let resolver = if cfg!(windows) { "where" } else { "which" };
Command::new(resolver)
.arg(command)
.output()
.map(|out| out.status.success())
.unwrap_or(false)
}
pub fn normalize_path(path: &Path) -> PathBuf {
let mut out = PathBuf::new();
for component in path.components() {
match component {
Component::CurDir => {}
Component::ParentDir => {
out.pop();
}
other => out.push(other.as_os_str()),
}
}
out
}
pub fn resolve_cli_project_context(g: &GlobalArgs) -> ProjectContext {
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let project_arg = g.project.clone().unwrap_or_else(|| ".".to_string());
let workspace_root = normalize_path(&cwd.join(&project_arg))
.to_string_lossy()
.to_string();
let settings = ProjectSettings {
sdk_path: g.sdk_root.clone().unwrap_or_default(),
python_path: String::new(),
board_yaml_path: g
.board_yaml
.clone()
.unwrap_or_else(|| "board.yaml".to_string()),
west_cwd: String::new(),
};
resolve_project_context(
&ProjectResolutionInput {
workspace_folders: vec![workspace_root],
settings,
is_windows: cfg!(windows),
},
|p| Path::new(p).exists(),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalize_collapses_current_and_parent_dirs() {
assert_eq!(
normalize_path(Path::new("/a/b/./c")),
PathBuf::from("/a/b/c")
);
assert_eq!(
normalize_path(Path::new("/a/b/../c")),
PathBuf::from("/a/c")
);
}
}