use std::path::{Path, PathBuf};
use anyhow::anyhow;
use espforge_diagnostics::Diagnostic;
use espforge_emit::ManifestDrift;
use espforge_emit::OverlayFile;
use espforge_emit::codegen::overlay::OverlayError;
use crate::pipeline::Checked;
use crate::source::Source;
#[derive(Debug)]
pub enum ProjectError {
Spec(Vec<Diagnostic>),
User(String),
Internal(anyhow::Error),
}
impl From<anyhow::Error> for ProjectError {
fn from(e: anyhow::Error) -> Self {
ProjectError::Internal(e)
}
}
impl From<std::io::Error> for ProjectError {
fn from(e: std::io::Error) -> Self {
ProjectError::Internal(e.into())
}
}
#[derive(Debug)]
pub struct Written {
pub crate_name: String,
}
pub fn generate(
source: &Source,
checked: &Checked,
board: &espforge_model::Board,
runtime_path: Option<PathBuf>,
out: &Path,
) -> Result<Written, ProjectError> {
let runtime = crate::source::runtime_source(runtime_path).map_err(ProjectError::User)?;
let overlay_owned =
crate::source::overlay_files(source).map_err(|e| ProjectError::User(format!("{e}")))?;
let overlay: Vec<OverlayFile> = overlay_owned
.iter()
.map(|(path, contents)| OverlayFile { path, contents })
.collect();
let app = crate::source::app_logic(source).map_err(ProjectError::User)?;
let generated = espforge_emit::generate_from_board(
board,
&checked.meta,
&checked.resolved,
&runtime,
&overlay,
)
.map_err(|e| match e.downcast_ref::<OverlayError>() {
Some(overlay) => ProjectError::User(overlay.to_string()),
None => ProjectError::Internal(e),
})?;
espforge_emit::write_all(out, &generated).map_err(|e| {
match e.downcast_ref::<ManifestDrift>() {
Some(drift) => ProjectError::User(drift.to_string()),
None => ProjectError::Internal(e),
}
})?;
std::fs::create_dir_all(out.join("src"))?;
std::fs::write(out.join("src").join("app.rs"), app)?;
Ok(Written {
crate_name: checked.meta.crate_name(),
})
}
pub fn write_spec(checked: &Checked, path: &Path) -> Result<(), ProjectError> {
let doc = crate::spec::effective_document(
&checked.fe.parsed.sections,
&checked.meta,
&checked.answers,
)
.map_err(|errors| {
ProjectError::Spec(errors.iter().map(|e| e.to_diagnostic()).collect::<Vec<_>>())
})?;
let text = crate::spec::render(&doc).map_err(|e| ProjectError::Internal(anyhow!("{e}")))?;
std::fs::write(path, text)
.map_err(|e| ProjectError::Internal(anyhow!("cannot write '{}': {e}", path.display())))?;
Ok(())
}