config.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. use crate::{cfg::Platform, error::Result};
  2. use manganis_cli_support::AssetManifest;
  3. use manganis_cli_support::AssetManifestExt;
  4. use serde::{Deserialize, Serialize};
  5. use std::{
  6. collections::HashMap,
  7. path::{Path, PathBuf},
  8. };
  9. #[derive(Debug, Clone, Serialize, Deserialize)]
  10. pub struct DioxusConfig {
  11. pub application: ApplicationConfig,
  12. pub web: WebConfig,
  13. #[serde(default)]
  14. pub bundle: BundleConfig,
  15. #[serde(default = "default_plugin")]
  16. pub plugin: toml::Value,
  17. }
  18. fn default_plugin() -> toml::Value {
  19. toml::Value::Boolean(true)
  20. }
  21. impl DioxusConfig {
  22. pub fn load(bin: Option<PathBuf>) -> crate::error::Result<Option<DioxusConfig>> {
  23. let crate_dir = crate::cargo::crate_root();
  24. let crate_dir = match crate_dir {
  25. Ok(dir) => {
  26. if let Some(bin) = bin {
  27. dir.join(bin)
  28. } else {
  29. dir
  30. }
  31. }
  32. Err(_) => return Ok(None),
  33. };
  34. let crate_dir = crate_dir.as_path();
  35. let Some(dioxus_conf_file) = acquire_dioxus_toml(crate_dir) else {
  36. return Ok(None);
  37. };
  38. let dioxus_conf_file = dioxus_conf_file.as_path();
  39. let cfg = toml::from_str::<DioxusConfig>(&std::fs::read_to_string(dioxus_conf_file)?)
  40. .map_err(|err| {
  41. let error_location = dioxus_conf_file
  42. .strip_prefix(crate_dir)
  43. .unwrap_or(dioxus_conf_file)
  44. .display();
  45. crate::Error::Unique(format!("{error_location} {err}"))
  46. })
  47. .map(Some);
  48. match cfg {
  49. Ok(Some(mut cfg)) => {
  50. let name = cfg.application.name.clone();
  51. if cfg.bundle.identifier.is_none() {
  52. cfg.bundle.identifier = Some(format!("io.github.{name}"));
  53. }
  54. if cfg.bundle.publisher.is_none() {
  55. cfg.bundle.publisher = Some(name);
  56. }
  57. Ok(Some(cfg))
  58. }
  59. cfg => cfg,
  60. }
  61. }
  62. }
  63. fn acquire_dioxus_toml(dir: &Path) -> Option<PathBuf> {
  64. // prefer uppercase
  65. let uppercase_conf = dir.join("Dioxus.toml");
  66. if uppercase_conf.is_file() {
  67. return Some(uppercase_conf);
  68. }
  69. // lowercase is fine too
  70. let lowercase_conf = dir.join("dioxus.toml");
  71. if lowercase_conf.is_file() {
  72. return Some(lowercase_conf);
  73. }
  74. None
  75. }
  76. impl Default for DioxusConfig {
  77. fn default() -> Self {
  78. let name = "name";
  79. Self {
  80. application: ApplicationConfig {
  81. name: name.into(),
  82. default_platform: Platform::Web,
  83. out_dir: Some(PathBuf::from("dist")),
  84. asset_dir: Some(PathBuf::from("public")),
  85. tools: None,
  86. sub_package: None,
  87. },
  88. web: WebConfig {
  89. app: WebAppConfig {
  90. title: Some("dioxus | ⛺".into()),
  91. base_path: None,
  92. },
  93. proxy: Some(vec![]),
  94. watcher: WebWatcherConfig {
  95. watch_path: Some(vec![PathBuf::from("src"), PathBuf::from("examples")]),
  96. reload_html: Some(false),
  97. index_on_404: Some(true),
  98. },
  99. resource: WebResourceConfig {
  100. dev: WebDevResourceConfig {
  101. style: Some(vec![]),
  102. script: Some(vec![]),
  103. },
  104. style: Some(vec![]),
  105. script: Some(vec![]),
  106. },
  107. https: WebHttpsConfig {
  108. enabled: None,
  109. mkcert: None,
  110. key_path: None,
  111. cert_path: None,
  112. },
  113. },
  114. bundle: BundleConfig {
  115. identifier: Some(format!("io.github.{name}")),
  116. publisher: Some(name.into()),
  117. ..Default::default()
  118. },
  119. plugin: toml::Value::Table(toml::map::Map::new()),
  120. }
  121. }
  122. }
  123. #[derive(Debug, Clone, Serialize, Deserialize)]
  124. pub struct ApplicationConfig {
  125. pub name: String,
  126. pub default_platform: Platform,
  127. pub out_dir: Option<PathBuf>,
  128. pub asset_dir: Option<PathBuf>,
  129. pub tools: Option<HashMap<String, toml::Value>>,
  130. pub sub_package: Option<String>,
  131. }
  132. #[derive(Debug, Clone, Serialize, Deserialize)]
  133. pub struct WebConfig {
  134. pub app: WebAppConfig,
  135. pub proxy: Option<Vec<WebProxyConfig>>,
  136. pub watcher: WebWatcherConfig,
  137. pub resource: WebResourceConfig,
  138. #[serde(default)]
  139. pub https: WebHttpsConfig,
  140. }
  141. #[derive(Debug, Clone, Serialize, Deserialize)]
  142. pub struct WebAppConfig {
  143. pub title: Option<String>,
  144. pub base_path: Option<String>,
  145. }
  146. #[derive(Debug, Clone, Serialize, Deserialize)]
  147. pub struct WebProxyConfig {
  148. pub backend: String,
  149. }
  150. #[derive(Debug, Clone, Serialize, Deserialize)]
  151. pub struct WebWatcherConfig {
  152. pub watch_path: Option<Vec<PathBuf>>,
  153. pub reload_html: Option<bool>,
  154. pub index_on_404: Option<bool>,
  155. }
  156. #[derive(Debug, Clone, Serialize, Deserialize)]
  157. pub struct WebResourceConfig {
  158. pub dev: WebDevResourceConfig,
  159. pub style: Option<Vec<PathBuf>>,
  160. pub script: Option<Vec<PathBuf>>,
  161. }
  162. #[derive(Debug, Clone, Serialize, Deserialize)]
  163. pub struct WebDevResourceConfig {
  164. pub style: Option<Vec<PathBuf>>,
  165. pub script: Option<Vec<PathBuf>>,
  166. }
  167. #[derive(Debug, Default, Clone, Serialize, Deserialize)]
  168. pub struct WebHttpsConfig {
  169. pub enabled: Option<bool>,
  170. pub mkcert: Option<bool>,
  171. pub key_path: Option<String>,
  172. pub cert_path: Option<String>,
  173. }
  174. #[derive(Debug, Clone)]
  175. pub struct CrateConfig {
  176. pub out_dir: PathBuf,
  177. pub crate_dir: PathBuf,
  178. pub workspace_dir: PathBuf,
  179. pub target_dir: PathBuf,
  180. pub asset_dir: PathBuf,
  181. pub manifest: cargo_toml::Manifest<cargo_toml::Value>,
  182. pub executable: ExecutableType,
  183. pub dioxus_config: DioxusConfig,
  184. pub release: bool,
  185. pub hot_reload: bool,
  186. pub cross_origin_policy: bool,
  187. pub verbose: bool,
  188. pub custom_profile: Option<String>,
  189. pub features: Option<Vec<String>>,
  190. pub target: Option<String>,
  191. pub cargo_args: Vec<String>,
  192. }
  193. #[derive(Debug, Clone)]
  194. pub enum ExecutableType {
  195. Binary(String),
  196. Lib(String),
  197. Example(String),
  198. }
  199. impl CrateConfig {
  200. pub fn new(bin: Option<PathBuf>) -> Result<Self> {
  201. let dioxus_config = DioxusConfig::load(bin.clone())?.unwrap_or_default();
  202. let crate_root = crate::cargo::crate_root()?;
  203. let crate_dir = if let Some(package) = &dioxus_config.application.sub_package {
  204. crate_root.join(package)
  205. } else if let Some(bin) = bin {
  206. crate_root.join(bin)
  207. } else {
  208. crate_root
  209. };
  210. let meta = crate::cargo::Metadata::get()?;
  211. let workspace_dir = meta.workspace_root;
  212. let target_dir = meta.target_directory;
  213. let out_dir = match dioxus_config.application.out_dir {
  214. Some(ref v) => crate_dir.join(v),
  215. None => crate_dir.join("dist"),
  216. };
  217. let cargo_def = &crate_dir.join("Cargo.toml");
  218. let asset_dir = match dioxus_config.application.asset_dir {
  219. Some(ref v) => crate_dir.join(v),
  220. None => crate_dir.join("public"),
  221. };
  222. let manifest = cargo_toml::Manifest::from_path(cargo_def).unwrap();
  223. let mut output_filename = String::from("dioxus_app");
  224. if let Some(package) = &manifest.package.as_ref() {
  225. output_filename = match &package.default_run {
  226. Some(default_run_target) => default_run_target.to_owned(),
  227. None => manifest
  228. .bin
  229. .iter()
  230. .find(|b| b.name == manifest.package.as_ref().map(|pkg| pkg.name.clone()))
  231. .or(manifest
  232. .bin
  233. .iter()
  234. .find(|b| b.path == Some("src/main.rs".to_owned())))
  235. .or(manifest.bin.first())
  236. .or(manifest.lib.as_ref())
  237. .and_then(|prod| prod.name.clone())
  238. .unwrap_or(String::from("dioxus_app")),
  239. };
  240. }
  241. let executable = ExecutableType::Binary(output_filename);
  242. let release = false;
  243. let hot_reload = false;
  244. let verbose = false;
  245. let custom_profile = None;
  246. let features = None;
  247. let target = None;
  248. let cargo_args = vec![];
  249. Ok(Self {
  250. out_dir,
  251. crate_dir,
  252. workspace_dir,
  253. target_dir,
  254. asset_dir,
  255. manifest,
  256. executable,
  257. release,
  258. dioxus_config,
  259. hot_reload,
  260. cross_origin_policy: false,
  261. custom_profile,
  262. features,
  263. verbose,
  264. target,
  265. cargo_args,
  266. })
  267. }
  268. pub fn asset_manifest(&self) -> AssetManifest {
  269. AssetManifest::load_from_path(
  270. self.crate_dir.join("Cargo.toml"),
  271. self.workspace_dir.join("Cargo.lock"),
  272. )
  273. }
  274. pub fn as_example(&mut self, example_name: String) -> &mut Self {
  275. self.executable = ExecutableType::Example(example_name);
  276. self
  277. }
  278. pub fn with_release(&mut self, release: bool) -> &mut Self {
  279. self.release = release;
  280. self
  281. }
  282. pub fn with_hot_reload(&mut self, hot_reload: bool) -> &mut Self {
  283. self.hot_reload = hot_reload;
  284. self
  285. }
  286. pub fn with_cross_origin_policy(&mut self, cross_origin_policy: bool) -> &mut Self {
  287. self.cross_origin_policy = cross_origin_policy;
  288. self
  289. }
  290. pub fn with_verbose(&mut self, verbose: bool) -> &mut Self {
  291. self.verbose = verbose;
  292. self
  293. }
  294. pub fn set_profile(&mut self, profile: String) -> &mut Self {
  295. self.custom_profile = Some(profile);
  296. self
  297. }
  298. pub fn set_features(&mut self, features: Vec<String>) -> &mut Self {
  299. self.features = Some(features);
  300. self
  301. }
  302. pub fn set_target(&mut self, target: String) -> &mut Self {
  303. self.target = Some(target);
  304. self
  305. }
  306. pub fn set_cargo_args(&mut self, cargo_args: Vec<String>) -> &mut Self {
  307. self.cargo_args = cargo_args;
  308. self
  309. }
  310. }
  311. #[derive(Debug, Clone, Serialize, Deserialize, Default)]
  312. pub struct BundleConfig {
  313. pub identifier: Option<String>,
  314. pub publisher: Option<String>,
  315. pub icon: Option<Vec<String>>,
  316. pub resources: Option<Vec<String>>,
  317. pub copyright: Option<String>,
  318. pub category: Option<String>,
  319. pub short_description: Option<String>,
  320. pub long_description: Option<String>,
  321. pub external_bin: Option<Vec<String>>,
  322. pub deb: Option<DebianSettings>,
  323. pub macos: Option<MacOsSettings>,
  324. pub windows: Option<WindowsSettings>,
  325. }
  326. impl From<BundleConfig> for tauri_bundler::BundleSettings {
  327. fn from(val: BundleConfig) -> Self {
  328. tauri_bundler::BundleSettings {
  329. identifier: val.identifier,
  330. publisher: val.publisher,
  331. icon: val.icon,
  332. resources: val.resources,
  333. copyright: val.copyright,
  334. category: val.category.and_then(|c| c.parse().ok()),
  335. short_description: val.short_description,
  336. long_description: val.long_description,
  337. external_bin: val.external_bin,
  338. deb: val.deb.map(Into::into).unwrap_or_default(),
  339. macos: val.macos.map(Into::into).unwrap_or_default(),
  340. windows: val.windows.map(Into::into).unwrap_or_default(),
  341. ..Default::default()
  342. }
  343. }
  344. }
  345. #[derive(Debug, Clone, Serialize, Deserialize, Default)]
  346. pub struct DebianSettings {
  347. pub depends: Option<Vec<String>>,
  348. pub files: HashMap<PathBuf, PathBuf>,
  349. pub nsis: Option<NsisSettings>,
  350. }
  351. impl From<DebianSettings> for tauri_bundler::DebianSettings {
  352. fn from(val: DebianSettings) -> Self {
  353. tauri_bundler::DebianSettings {
  354. depends: val.depends,
  355. files: val.files,
  356. desktop_template: None,
  357. }
  358. }
  359. }
  360. #[derive(Debug, Clone, Serialize, Deserialize, Default)]
  361. pub struct WixSettings {
  362. pub language: Vec<(String, Option<PathBuf>)>,
  363. pub template: Option<PathBuf>,
  364. pub fragment_paths: Vec<PathBuf>,
  365. pub component_group_refs: Vec<String>,
  366. pub component_refs: Vec<String>,
  367. pub feature_group_refs: Vec<String>,
  368. pub feature_refs: Vec<String>,
  369. pub merge_refs: Vec<String>,
  370. pub skip_webview_install: bool,
  371. pub license: Option<PathBuf>,
  372. pub enable_elevated_update_task: bool,
  373. pub banner_path: Option<PathBuf>,
  374. pub dialog_image_path: Option<PathBuf>,
  375. pub fips_compliant: bool,
  376. }
  377. impl From<WixSettings> for tauri_bundler::WixSettings {
  378. fn from(val: WixSettings) -> Self {
  379. tauri_bundler::WixSettings {
  380. language: tauri_bundler::bundle::WixLanguage({
  381. let mut languages: Vec<_> = val
  382. .language
  383. .iter()
  384. .map(|l| {
  385. (
  386. l.0.clone(),
  387. tauri_bundler::bundle::WixLanguageConfig {
  388. locale_path: l.1.clone(),
  389. },
  390. )
  391. })
  392. .collect();
  393. if languages.is_empty() {
  394. languages.push(("en-US".into(), Default::default()));
  395. }
  396. languages
  397. }),
  398. template: val.template,
  399. fragment_paths: val.fragment_paths,
  400. component_group_refs: val.component_group_refs,
  401. component_refs: val.component_refs,
  402. feature_group_refs: val.feature_group_refs,
  403. feature_refs: val.feature_refs,
  404. merge_refs: val.merge_refs,
  405. skip_webview_install: val.skip_webview_install,
  406. license: val.license,
  407. enable_elevated_update_task: val.enable_elevated_update_task,
  408. banner_path: val.banner_path,
  409. dialog_image_path: val.dialog_image_path,
  410. fips_compliant: val.fips_compliant,
  411. }
  412. }
  413. }
  414. #[derive(Debug, Clone, Serialize, Deserialize, Default)]
  415. pub struct MacOsSettings {
  416. pub frameworks: Option<Vec<String>>,
  417. pub minimum_system_version: Option<String>,
  418. pub license: Option<String>,
  419. pub exception_domain: Option<String>,
  420. pub signing_identity: Option<String>,
  421. pub provider_short_name: Option<String>,
  422. pub entitlements: Option<String>,
  423. pub info_plist_path: Option<PathBuf>,
  424. }
  425. impl From<MacOsSettings> for tauri_bundler::MacOsSettings {
  426. fn from(val: MacOsSettings) -> Self {
  427. tauri_bundler::MacOsSettings {
  428. frameworks: val.frameworks,
  429. minimum_system_version: val.minimum_system_version,
  430. license: val.license,
  431. exception_domain: val.exception_domain,
  432. signing_identity: val.signing_identity,
  433. provider_short_name: val.provider_short_name,
  434. entitlements: val.entitlements,
  435. info_plist_path: val.info_plist_path,
  436. }
  437. }
  438. }
  439. #[derive(Debug, Clone, Serialize, Deserialize)]
  440. pub struct WindowsSettings {
  441. pub digest_algorithm: Option<String>,
  442. pub certificate_thumbprint: Option<String>,
  443. pub timestamp_url: Option<String>,
  444. pub tsp: bool,
  445. pub wix: Option<WixSettings>,
  446. pub icon_path: Option<PathBuf>,
  447. pub webview_install_mode: WebviewInstallMode,
  448. pub webview_fixed_runtime_path: Option<PathBuf>,
  449. pub allow_downgrades: bool,
  450. pub nsis: Option<NsisSettings>,
  451. }
  452. impl From<WindowsSettings> for tauri_bundler::WindowsSettings {
  453. fn from(val: WindowsSettings) -> Self {
  454. tauri_bundler::WindowsSettings {
  455. digest_algorithm: val.digest_algorithm,
  456. certificate_thumbprint: val.certificate_thumbprint,
  457. timestamp_url: val.timestamp_url,
  458. tsp: val.tsp,
  459. wix: val.wix.map(Into::into),
  460. icon_path: val.icon_path.unwrap_or("icons/icon.ico".into()),
  461. webview_install_mode: val.webview_install_mode.into(),
  462. webview_fixed_runtime_path: val.webview_fixed_runtime_path,
  463. allow_downgrades: val.allow_downgrades,
  464. nsis: val.nsis.map(Into::into),
  465. }
  466. }
  467. }
  468. #[derive(Debug, Clone, Serialize, Deserialize)]
  469. pub struct NsisSettings {
  470. pub template: Option<PathBuf>,
  471. pub license: Option<PathBuf>,
  472. pub header_image: Option<PathBuf>,
  473. pub sidebar_image: Option<PathBuf>,
  474. pub installer_icon: Option<PathBuf>,
  475. pub install_mode: NSISInstallerMode,
  476. pub languages: Option<Vec<String>>,
  477. pub custom_language_files: Option<HashMap<String, PathBuf>>,
  478. pub display_language_selector: bool,
  479. }
  480. impl From<NsisSettings> for tauri_bundler::NsisSettings {
  481. fn from(val: NsisSettings) -> Self {
  482. tauri_bundler::NsisSettings {
  483. license: val.license,
  484. header_image: val.header_image,
  485. sidebar_image: val.sidebar_image,
  486. installer_icon: val.installer_icon,
  487. install_mode: val.install_mode.into(),
  488. languages: val.languages,
  489. display_language_selector: val.display_language_selector,
  490. custom_language_files: None,
  491. template: None,
  492. compression: None,
  493. }
  494. }
  495. }
  496. #[derive(Debug, Clone, Serialize, Deserialize)]
  497. pub enum NSISInstallerMode {
  498. CurrentUser,
  499. PerMachine,
  500. Both,
  501. }
  502. impl From<NSISInstallerMode> for tauri_utils::config::NSISInstallerMode {
  503. fn from(val: NSISInstallerMode) -> Self {
  504. match val {
  505. NSISInstallerMode::CurrentUser => tauri_utils::config::NSISInstallerMode::CurrentUser,
  506. NSISInstallerMode::PerMachine => tauri_utils::config::NSISInstallerMode::PerMachine,
  507. NSISInstallerMode::Both => tauri_utils::config::NSISInstallerMode::Both,
  508. }
  509. }
  510. }
  511. #[derive(Debug, Clone, Serialize, Deserialize)]
  512. pub enum WebviewInstallMode {
  513. Skip,
  514. DownloadBootstrapper { silent: bool },
  515. EmbedBootstrapper { silent: bool },
  516. OfflineInstaller { silent: bool },
  517. FixedRuntime { path: PathBuf },
  518. }
  519. impl WebviewInstallMode {
  520. fn into(self) -> tauri_utils::config::WebviewInstallMode {
  521. match self {
  522. Self::Skip => tauri_utils::config::WebviewInstallMode::Skip,
  523. Self::DownloadBootstrapper { silent } => {
  524. tauri_utils::config::WebviewInstallMode::DownloadBootstrapper { silent }
  525. }
  526. Self::EmbedBootstrapper { silent } => {
  527. tauri_utils::config::WebviewInstallMode::EmbedBootstrapper { silent }
  528. }
  529. Self::OfflineInstaller { silent } => {
  530. tauri_utils::config::WebviewInstallMode::OfflineInstaller { silent }
  531. }
  532. Self::FixedRuntime { path } => {
  533. tauri_utils::config::WebviewInstallMode::FixedRuntime { path }
  534. }
  535. }
  536. }
  537. }
  538. impl Default for WebviewInstallMode {
  539. fn default() -> Self {
  540. Self::OfflineInstaller { silent: false }
  541. }
  542. }