use std::path::{Path, PathBuf};
use color_eyre::eyre;
use serde::{Deserialize, Serialize};
use crate::{
backend::Backend,
build::BuildOptions,
device::Artifact,
hydrolysis::platform::{
build_hydrolysis, clean_hydrolysis, is_hydrolysis_platform, package_hydrolysis,
},
platform::{PackageOptions, TargetBackend, TargetPlatform},
project::Project,
templates::{self, TemplateContext},
};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct HydrolysisBackend {
#[serde(
default = "default_hydrolysis_project_path",
skip_serializing_if = "is_default_hydrolysis_project_path"
)]
project_path: PathBuf,
}
impl HydrolysisBackend {
#[must_use]
pub fn new() -> Self {
Self {
project_path: default_hydrolysis_project_path(),
}
}
#[must_use]
pub fn with_project_path(mut self, path: impl Into<PathBuf>) -> Self {
self.project_path = path.into();
self
}
#[must_use]
pub const fn project_path(&self) -> &PathBuf {
&self.project_path
}
pub async fn requires_regeneration(project: &Project) -> eyre::Result<bool> {
let backend_dir = project.backend_path::<Self>();
let ctx = Self::template_context(project).await?;
let outputs = templates::hydrolysis::rendered_outputs(
&ctx,
&project.hydrolysis_backend_crate_name(),
)?;
for (relative, expected) in outputs {
let path = backend_dir.join(&relative);
let per_run_binding = relative == Path::new("src/preview_symbol.rs")
|| relative == Path::new("src/preview_test.rs");
match std::fs::read(&path) {
Ok(existing) if per_run_binding || existing == expected => {}
Ok(_) | Err(_) => return Ok(true),
}
}
Ok(false)
}
async fn template_context(project: &Project) -> eyre::Result<TemplateContext> {
let manifest = project.manifest();
let app_name = manifest
.package
.name
.chars()
.filter(|c| c.is_alphanumeric())
.collect::<String>();
Ok(
TemplateContext::for_project_manifest(manifest, project.crate_name().clone(), app_name)
.with_backend_project_path(project.backend_path::<Self>())
.with_project_root_path(project.root().to_path_buf())
.with_webview_enabled(project.links_runtime_package("waterui-webview").await?)
.with_chromium_enabled(project.links_runtime_package("waterui-chromium").await?)
.with_browser_engine(project.linked_browser_engine().await?),
)
}
}
impl Default for HydrolysisBackend {
fn default() -> Self {
Self::new()
}
}
impl Backend for HydrolysisBackend {
const DEFAULT_PATH: &'static str = "hydrolysis";
const CACHE_PATHS: &'static [&'static str] = &["Cargo.lock"];
fn path(&self) -> &Path {
&self.project_path
}
async fn init(project: &Project) -> Result<Self, crate::backend::FailToInitBackend> {
let project_path = default_hydrolysis_project_path();
let ctx = Self::template_context(project)
.await
.map_err(crate::backend::FailToInitBackend::Config)?;
templates::hydrolysis::scaffold(
&project.backend_path::<Self>(),
&ctx,
&project.hydrolysis_backend_crate_name(),
)
.await
.map_err(crate::backend::FailToInitBackend::Io)?;
Ok(Self { project_path })
}
fn supports(&self, platform: TargetPlatform) -> bool {
is_hydrolysis_platform(platform)
}
async fn build(
&self,
project: &Project,
platform: TargetPlatform,
options: BuildOptions,
) -> eyre::Result<PathBuf> {
project
.browser_runtime_plan(platform, TargetBackend::Hydrolysis)
.await?;
build_hydrolysis(project, platform, options).await
}
async fn package(
&self,
project: &Project,
platform: TargetPlatform,
options: PackageOptions,
) -> eyre::Result<Artifact> {
package_hydrolysis(project, platform, options).await
}
async fn clean(&self, project: &Project, _platform: TargetPlatform) -> eyre::Result<()> {
clean_hydrolysis(project).await
}
}
fn default_hydrolysis_project_path() -> PathBuf {
PathBuf::from("hydrolysis")
}
fn is_default_hydrolysis_project_path(s: &Path) -> bool {
s == Path::new("hydrolysis")
}