use moon_common::Id;
use moon_project::ProjectFragment;
use std::time::{Duration, Instant, SystemTime};
use warpgate_api::{AnyResult, VirtualPath, api_enum, api_struct};
api_struct!(
pub struct MoonContext {
pub working_dir: VirtualPath,
pub workspace_root: VirtualPath,
}
);
impl MoonContext {
pub fn get_absolute_path<T: AsRef<std::path::Path>>(&self, path: T) -> VirtualPath {
let path = path.as_ref();
if path.is_absolute() {
return VirtualPath::Real(path.to_owned());
}
self.working_dir.join(path)
}
pub fn get_project_root(&self, project: &ProjectFragment) -> VirtualPath {
self.get_project_root_from_source(&project.source)
}
pub fn get_project_root_from_source(&self, source: &str) -> VirtualPath {
if source.is_empty() || source == "." {
self.workspace_root.clone()
} else {
self.workspace_root.join(source)
}
}
}
api_enum!(
#[derive(Default)]
pub enum OperationStatus {
#[default]
Pending,
Failed,
Passed,
}
);
api_struct!(
#[serde(default)]
pub struct Operation {
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<Duration>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub finished_at: Option<SystemTime>,
pub id: Id,
#[serde(skip_serializing_if = "Option::is_none")]
pub started_at: Option<SystemTime>,
#[serde(skip)]
pub start_time: Option<Instant>,
pub status: OperationStatus,
}
);
impl Operation {
pub fn new(id: impl AsRef<str>) -> AnyResult<Self> {
Ok(Operation {
duration: None,
error: None,
finished_at: None,
id: Id::new(id.as_ref())?,
started_at: Some(SystemTime::now()),
start_time: Some(Instant::now()),
status: OperationStatus::Pending,
})
}
pub fn finish(&mut self, status: OperationStatus) {
self.finished_at = Some(SystemTime::now());
self.status = status;
if let Some(start) = &self.start_time {
self.duration = Some(start.elapsed());
}
}
pub fn finish_with_result<T, E: std::fmt::Display>(&mut self, result: &Result<T, E>) {
match result {
Ok(_) => {
self.finish(OperationStatus::Passed);
}
Err(error) => {
self.finish(OperationStatus::Failed);
self.error = Some(error.to_string());
}
}
}
pub fn track<I, F, R>(id: I, func: F) -> AnyResult<(Self, R)>
where
I: AsRef<str>,
F: FnOnce() -> AnyResult<R>,
{
let mut op = Self::new(id)?;
match func() {
Ok(res) => {
op.finish(OperationStatus::Passed);
Ok((op, res))
}
Err(error) => Err(error),
}
}
pub fn track_with_error<I, F, R>(id: I, func: F) -> AnyResult<(Self, Option<R>)>
where
I: AsRef<str>,
F: FnOnce() -> AnyResult<R>,
{
let mut op = Self::new(id)?;
let res = match func() {
Ok(res) => {
op.finish(OperationStatus::Passed);
(op, Some(res))
}
Err(error) => {
op.finish(OperationStatus::Failed);
op.error = Some(error.to_string());
(op, None)
}
};
Ok(res)
}
}