config.rs 18 KB

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