settings.rs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. use crate::{Result, TraceSrc};
  2. use once_cell::sync::Lazy;
  3. use serde::{Deserialize, Serialize};
  4. use std::{fs, path::PathBuf, sync::Arc};
  5. use tracing::{error, trace, warn};
  6. const GLOBAL_SETTINGS_FILE_NAME: &str = "dioxus/settings.toml";
  7. /// Describes cli settings from project or global level.
  8. /// The order of priority goes:
  9. /// 1. CLI Flags/Arguments
  10. /// 2. Project-level Settings
  11. /// 3. Global-level settings.
  12. ///
  13. /// This allows users to control the cli settings with ease.
  14. #[derive(Debug, Clone, Serialize, Deserialize, Default)]
  15. pub(crate) struct CliSettings {
  16. /// Describes whether hot reload should always be on.
  17. pub(crate) always_hot_reload: Option<bool>,
  18. /// Describes whether the CLI should always open the browser for Web targets.
  19. pub(crate) always_open_browser: Option<bool>,
  20. /// Describes whether desktop apps in development will be pinned always-on-top.
  21. pub(crate) always_on_top: Option<bool>,
  22. /// Describes the interval in seconds that the CLI should poll for file changes on WSL.
  23. #[serde(default = "default_wsl_file_poll_interval")]
  24. pub(crate) wsl_file_poll_interval: Option<u16>,
  25. /// Use tooling from path rather than downloading them.
  26. pub(crate) no_downloads: Option<bool>,
  27. }
  28. impl CliSettings {
  29. /// Load the settings from the local, global, or default config in that order
  30. pub(crate) fn load() -> Arc<Self> {
  31. static SETTINGS: Lazy<Arc<CliSettings>> =
  32. Lazy::new(|| Arc::new(CliSettings::global_or_default()));
  33. SETTINGS.clone()
  34. }
  35. pub fn global_or_default() -> Self {
  36. CliSettings::from_global().unwrap_or_default()
  37. }
  38. /// Get the current settings structure from global.
  39. pub(crate) fn from_global() -> Option<Self> {
  40. let Some(path) = dirs::data_local_dir() else {
  41. warn!("failed to get local data directory, some config keys may be missing");
  42. return None;
  43. };
  44. let path = path.join(GLOBAL_SETTINGS_FILE_NAME);
  45. let Some(data) = fs::read_to_string(path).ok() else {
  46. // We use a debug here because we expect the file to not exist.
  47. trace!("failed to read `{}` config file", GLOBAL_SETTINGS_FILE_NAME);
  48. return None;
  49. };
  50. let data = toml::from_str::<CliSettings>(&data).ok();
  51. if data.is_none() {
  52. warn!(
  53. "failed to parse `{}` config file",
  54. GLOBAL_SETTINGS_FILE_NAME
  55. );
  56. }
  57. data
  58. }
  59. /// Save the current structure to the global settings toml.
  60. /// This does not save to project-level settings.
  61. pub(crate) fn save(&self) -> Result<()> {
  62. let path = Self::get_settings_path().ok_or_else(|| {
  63. error!(dx_src = ?TraceSrc::Dev, "failed to get settings path");
  64. anyhow::anyhow!("failed to get settings path")
  65. })?;
  66. let data = toml::to_string_pretty(&self).map_err(|e| {
  67. error!(dx_src = ?TraceSrc::Dev, ?self, "failed to parse config into toml");
  68. anyhow::anyhow!("failed to parse config into toml: {e}")
  69. })?;
  70. // Create the directory structure if it doesn't exist.
  71. let parent_path = path.parent().unwrap();
  72. if let Err(e) = fs::create_dir_all(parent_path) {
  73. error!(
  74. dx_src = ?TraceSrc::Dev,
  75. ?data,
  76. ?path,
  77. "failed to create directories for settings file"
  78. );
  79. return Err(
  80. anyhow::anyhow!("failed to create directories for settings file: {e}").into(),
  81. );
  82. }
  83. // Write the data.
  84. let result = fs::write(&path, data.clone());
  85. if let Err(e) = result {
  86. error!(?data, ?path, "failed to save global cli settings");
  87. return Err(anyhow::anyhow!("failed to save global cli settings: {e}").into());
  88. }
  89. Ok(())
  90. }
  91. /// Get the path to the settings toml file.
  92. pub(crate) fn get_settings_path() -> Option<PathBuf> {
  93. let Some(path) = dirs::data_local_dir() else {
  94. warn!("failed to get local data directory, some config keys may be missing");
  95. return None;
  96. };
  97. Some(path.join(GLOBAL_SETTINGS_FILE_NAME))
  98. }
  99. /// Modify the settings toml file - doesn't change the settings for this session
  100. pub(crate) fn modify_settings(with: impl FnOnce(&mut CliSettings)) -> Result<()> {
  101. let mut _settings = CliSettings::load();
  102. let settings: &mut CliSettings = Arc::make_mut(&mut _settings);
  103. with(settings);
  104. settings.save()?;
  105. Ok(())
  106. }
  107. /// Check if we should prefer to use the no-downloads feature
  108. pub(crate) fn prefer_no_downloads() -> bool {
  109. if cfg!(feature = "no-downloads") && !cfg!(debug_assertions) {
  110. return true;
  111. }
  112. if crate::devcfg::no_downloads() {
  113. return true;
  114. }
  115. CliSettings::load().no_downloads.unwrap_or_default()
  116. }
  117. }
  118. fn default_wsl_file_poll_interval() -> Option<u16> {
  119. Some(2)
  120. }