waterui_cli/gtk4/
backend.rs1use std::path::{Path, PathBuf};
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 backend::Backend,
9 build::BuildOptions,
10 device::Artifact,
11 gtk4::platform::{build_gtk4, clean_gtk4, is_gtk4_platform, package_gtk4},
12 platform::{PackageOptions, TargetBackend, TargetPlatform},
13 project::Project,
14 templates::{self, TemplateContext},
15};
16
17#[derive(Debug, Serialize, Deserialize, Clone)]
21pub struct Gtk4Backend {
22 #[serde(
23 default = "default_gtk4_project_path",
24 skip_serializing_if = "is_default_gtk4_project_path"
25 )]
26 project_path: PathBuf,
27}
28
29impl Gtk4Backend {
30 #[must_use]
32 pub fn new() -> Self {
33 Self {
34 project_path: default_gtk4_project_path(),
35 }
36 }
37
38 #[must_use]
40 pub fn with_project_path(mut self, path: impl Into<PathBuf>) -> Self {
41 self.project_path = path.into();
42 self
43 }
44
45 #[must_use]
47 pub const fn project_path(&self) -> &PathBuf {
48 &self.project_path
49 }
50
51 pub async fn requires_regeneration(project: &Project) -> eyre::Result<bool> {
58 let backend_dir = project.backend_path::<Self>();
59 let ctx = Self::template_context(project).await?;
60 for (relative, expected) in
61 templates::gtk4::rendered_outputs(&ctx, &project.gtk_backend_crate_name())?
62 {
63 match std::fs::read(backend_dir.join(relative)) {
64 Ok(existing) if existing == expected => {}
65 Ok(_) | Err(_) => return Ok(true),
66 }
67 }
68 Ok(false)
69 }
70
71 async fn template_context(project: &Project) -> eyre::Result<TemplateContext> {
72 let manifest = project.manifest();
73 let app_name = manifest
74 .package
75 .name
76 .chars()
77 .filter(|c| c.is_alphanumeric())
78 .collect::<String>();
79 Ok(TemplateContext::for_project_manifest(
80 manifest,
81 project.crate_name().clone(),
82 app_name,
83 &project.resolved_framework().await?,
84 )
85 .with_backend_project_path(project.backend_path::<Self>())
86 .with_project_root_path(project.root().to_path_buf())
87 .with_webview_enabled(project.uses_standard_webview().await?)
88 .with_chromium_enabled(project.links_runtime_package("waterui-chromium").await?)
89 .with_browser_engine(project.linked_browser_engine().await?))
90 }
91}
92
93impl Default for Gtk4Backend {
94 fn default() -> Self {
95 Self::new()
96 }
97}
98
99impl Backend for Gtk4Backend {
100 const DEFAULT_PATH: &'static str = "gtk4";
101
102 const CACHE_PATHS: &'static [&'static str] = &[];
106
107 fn path(&self) -> &Path {
108 &self.project_path
109 }
110
111 async fn init(project: &Project) -> Result<Self, crate::backend::FailToInitBackend> {
112 let project_path = default_gtk4_project_path();
113 let ctx = Self::template_context(project)
114 .await
115 .map_err(crate::backend::FailToInitBackend::Config)?;
116
117 templates::gtk4::scaffold(
118 &project.backend_path::<Self>(),
119 &ctx,
120 &project.gtk_backend_crate_name(),
121 )
122 .await
123 .map_err(crate::backend::FailToInitBackend::Io)?;
124
125 Ok(Self { project_path })
126 }
127
128 fn supports(&self, platform: TargetPlatform) -> bool {
129 is_gtk4_platform(platform)
130 }
131
132 async fn build(
133 &self,
134 project: &Project,
135 _platform: TargetPlatform,
136 options: BuildOptions,
137 ) -> eyre::Result<PathBuf> {
138 project
139 .browser_runtime_plan(TargetPlatform::Linux, TargetBackend::Gtk4)
140 .await?;
141 build_gtk4(project, options).await
142 }
143
144 async fn package(
145 &self,
146 project: &Project,
147 _platform: TargetPlatform,
148 options: PackageOptions,
149 ) -> eyre::Result<Artifact> {
150 package_gtk4(project, options).await
151 }
152
153 async fn clean(&self, project: &Project, _platform: TargetPlatform) -> eyre::Result<()> {
154 clean_gtk4(project).await
155 }
156}
157
158fn default_gtk4_project_path() -> PathBuf {
159 PathBuf::from("gtk4")
160}
161
162fn is_default_gtk4_project_path(s: &Path) -> bool {
163 s == Path::new("gtk4")
164}