waterui_cli/apple/
backend.rs1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 backend::Backend,
7 project::Project,
8 templates::{self, TemplateContext},
9};
10
11#[derive(Debug, Serialize, Deserialize, Clone)]
12pub struct AppleBackend {
17 #[serde(
18 default = "default_apple_project_path",
19 skip_serializing_if = "is_default_apple_project_path"
20 )]
21 pub project_path: PathBuf,
23 pub scheme: String,
25 #[serde(skip_serializing_if = "Option::is_none")]
29 pub branch: Option<String>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
35 pub revision: Option<String>,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub backend_path: Option<String>,
39}
40
41impl AppleBackend {
42 #[must_use]
44 pub fn new(scheme: impl Into<String>) -> Self {
45 Self {
46 project_path: default_apple_project_path(),
47 scheme: scheme.into(),
48 branch: None,
49 revision: None,
50 backend_path: None,
51 }
52 }
53
54 #[must_use]
56 pub fn with_project_path(mut self, path: impl Into<PathBuf>) -> Self {
57 self.project_path = path.into();
58 self
59 }
60
61 #[must_use]
63 pub fn with_backend_path(mut self, path: impl Into<String>) -> Self {
64 self.backend_path = Some(path.into());
65 self
66 }
67
68 #[must_use]
70 pub fn project_path(&self) -> &Path {
71 &self.project_path
72 }
73}
74
75fn default_apple_project_path() -> PathBuf {
76 PathBuf::from("apple")
77}
78
79fn is_default_apple_project_path(s: &Path) -> bool {
80 s == Path::new("apple")
81}
82
83impl Backend for AppleBackend {
84 const DEFAULT_PATH: &'static str = "apple";
85
86 fn path(&self) -> &Path {
87 &self.project_path
88 }
89
90 async fn init(project: &Project) -> Result<Self, crate::backend::FailToInitBackend> {
91 let manifest = project.manifest();
92
93 let is_playground =
96 manifest.package.package_type == crate::project::PackageType::Playground;
97
98 let (scheme, app_name, crate_name_for_template) = if is_playground {
101 (
102 "WaterUIApp".to_string(),
103 "WaterUIApp".to_string(),
104 "WaterUIApp".to_string(),
105 )
106 } else {
107 let crate_name = project.crate_name().to_string();
108 let app_name = crate_name
111 .split('-')
112 .map(|s| {
113 let mut chars = s.chars();
114 chars.next().map_or_else(String::new, |first| {
115 first.to_uppercase().chain(chars).collect()
116 })
117 })
118 .collect::<String>();
119 (crate_name.clone(), app_name, crate_name)
120 };
121
122 let backend_relative_path = project.backend_relative_path::<Self>();
124
125 let project_path = default_apple_project_path();
126
127 let ctx = TemplateContext {
128 app_display_name: manifest.package.name.clone(),
129 app_name,
130 crate_name: crate_name_for_template,
131 bundle_identifier: manifest.package.bundle_identifier.clone(),
132 author: String::new(),
133 android_backend_path: None,
134 use_remote_dev_backend: manifest.waterui_path.is_none(),
135 waterui_path: manifest.waterui_path.as_ref().map(PathBuf::from),
136 backend_project_path: Some(backend_relative_path),
137 android_permissions: Vec::new(),
138 };
139
140 templates::apple::scaffold(&project.backend_path::<Self>(), &ctx)
141 .await
142 .map_err(crate::backend::FailToInitBackend::Io)?;
143
144 Ok(Self {
145 project_path,
146 scheme,
147 branch: None,
148 revision: None,
149 backend_path: manifest.waterui_path.clone(),
150 })
151 }
152}