settings.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. use crate::{Result, TraceSrc};
  2. use serde::{Deserialize, Serialize};
  3. use std::sync::LazyLock;
  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. /// Ignore updates for this version
  28. pub(crate) ignore_version_update: Option<String>,
  29. /// Disable telemetry
  30. pub(crate) disable_telemetry: Option<bool>,
  31. }
  32. impl CliSettings {
  33. /// Load the settings from the local, global, or default config in that order
  34. pub(crate) fn load() -> Arc<Self> {
  35. static SETTINGS: LazyLock<Arc<CliSettings>> =
  36. LazyLock::new(|| Arc::new(CliSettings::global_or_default()));
  37. SETTINGS.clone()
  38. }
  39. pub fn global_or_default() -> Self {
  40. CliSettings::from_global().unwrap_or_default()
  41. }
  42. /// Get the current settings structure from global.
  43. pub(crate) fn from_global() -> Option<Self> {
  44. let Some(path) = dirs::data_local_dir() else {
  45. warn!("failed to get local data directory, some config keys may be missing");
  46. return None;
  47. };
  48. let path = path.join(GLOBAL_SETTINGS_FILE_NAME);
  49. let Some(data) = fs::read_to_string(path).ok() else {
  50. // We use a debug here because we expect the file to not exist.
  51. trace!("failed to read `{}` config file", GLOBAL_SETTINGS_FILE_NAME);
  52. return None;
  53. };
  54. let data = toml::from_str::<CliSettings>(&data).ok();
  55. if data.is_none() {
  56. warn!(
  57. "failed to parse `{}` config file",
  58. GLOBAL_SETTINGS_FILE_NAME
  59. );
  60. }
  61. data
  62. }
  63. /// Save the current structure to the global settings toml.
  64. /// This does not save to project-level settings.
  65. pub(crate) fn save(&self) -> Result<()> {
  66. let path = Self::get_settings_path().ok_or_else(|| {
  67. error!(dx_src = ?TraceSrc::Dev, "failed to get settings path");
  68. anyhow::anyhow!("failed to get settings path")
  69. })?;
  70. let data = toml::to_string_pretty(&self).map_err(|e| {
  71. error!(dx_src = ?TraceSrc::Dev, ?self, "failed to parse config into toml");
  72. anyhow::anyhow!("failed to parse config into toml: {e}")
  73. })?;
  74. // Create the directory structure if it doesn't exist.
  75. let parent_path = path.parent().unwrap();
  76. if let Err(e) = fs::create_dir_all(parent_path) {
  77. error!(
  78. dx_src = ?TraceSrc::Dev,
  79. ?data,
  80. ?path,
  81. "failed to create directories for settings file"
  82. );
  83. return Err(
  84. anyhow::anyhow!("failed to create directories for settings file: {e}").into(),
  85. );
  86. }
  87. // Write the data.
  88. let result = fs::write(&path, data.clone());
  89. if let Err(e) = result {
  90. error!(?data, ?path, "failed to save global cli settings");
  91. return Err(anyhow::anyhow!("failed to save global cli settings: {e}").into());
  92. }
  93. Ok(())
  94. }
  95. /// Get the path to the settings toml file.
  96. pub(crate) fn get_settings_path() -> Option<PathBuf> {
  97. let Some(path) = dirs::data_local_dir() else {
  98. warn!("failed to get local data directory, some config keys may be missing");
  99. return None;
  100. };
  101. Some(path.join(GLOBAL_SETTINGS_FILE_NAME))
  102. }
  103. /// Modify the settings toml file - doesn't change the settings for this session
  104. pub(crate) fn modify_settings(with: impl FnOnce(&mut CliSettings)) -> Result<()> {
  105. let mut _settings = CliSettings::load();
  106. let settings: &mut CliSettings = Arc::make_mut(&mut _settings);
  107. with(settings);
  108. settings.save()?;
  109. Ok(())
  110. }
  111. /// Check if we should prefer to use the no-downloads feature
  112. pub(crate) fn prefer_no_downloads() -> bool {
  113. if cfg!(feature = "no-downloads") && !cfg!(debug_assertions) {
  114. return true;
  115. }
  116. if crate::devcfg::no_downloads() {
  117. return true;
  118. }
  119. CliSettings::load().no_downloads.unwrap_or_default()
  120. }
  121. }
  122. fn default_wsl_file_poll_interval() -> Option<u16> {
  123. Some(2)
  124. }