first pass of merging in warp (doesn't build)
This commit is contained in:
+106
-39
@@ -4,21 +4,21 @@ pub mod manager;
|
||||
pub mod schema;
|
||||
|
||||
// Re-export commonly used types and traits
|
||||
pub use macros::SettingSection;
|
||||
pub use manager::SettingsManager;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
|
||||
// Re-export crates used by macro expansions in downstream crates.
|
||||
#[doc(hidden)]
|
||||
pub use inventory as _inventory;
|
||||
pub use macros::SettingSection;
|
||||
pub use manager::SettingsManager;
|
||||
#[doc(hidden)]
|
||||
pub use schemars as _schemars;
|
||||
#[doc(hidden)]
|
||||
pub use settings_value as _settings_value;
|
||||
pub use settings_value::SettingsValue;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
// Re-export galaxyui_core for use by macros.
|
||||
pub use galaxyui_core;
|
||||
|
||||
/// Extracts the storage key (last segment after the final `.`) from a toml_path.
|
||||
///
|
||||
@@ -59,31 +59,12 @@ pub const fn toml_path_hierarchy(path: &str) -> Option<&str> {
|
||||
}
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use galaxyui::{AppContext, Entity, ModelContext};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use galaxy_features::FeatureFlag;
|
||||
use galaxyui_core::{AppContext, Entity, ModelContext};
|
||||
use galaxyui_extras::secure_storage::{self, AppContextExt as _};
|
||||
use galaxyui_extras::user_preferences::UserPreferences;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
|
||||
/// Whether the TOML-backed settings file is active.
|
||||
///
|
||||
/// Set once during startup via [`set_settings_file_enabled`]. When `false`,
|
||||
/// public settings fall back to the private (platform-native) backend so
|
||||
/// that all settings share a single instance.
|
||||
static SETTINGS_FILE_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Records whether the TOML-backed settings file feature is active.
|
||||
///
|
||||
/// Call this once during startup after checking `FeatureFlag::SettingsFile`.
|
||||
/// The value is read by [`Setting::preferences_for_setting`] and
|
||||
/// [`SettingsManager::read_local_setting_value`] to decide which backend
|
||||
/// to use for public settings.
|
||||
pub fn set_settings_file_enabled(enabled: bool) {
|
||||
SETTINGS_FILE_ENABLED.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Returns whether the TOML-backed settings file is currently active.
|
||||
pub fn is_settings_file_enabled() -> bool {
|
||||
SETTINGS_FILE_ENABLED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// A newtype wrapper for the public preferences backend.
|
||||
///
|
||||
@@ -122,11 +103,11 @@ impl PublicPreferences {
|
||||
}
|
||||
}
|
||||
|
||||
impl galaxyui::Entity for PublicPreferences {
|
||||
impl galaxyui_core::Entity for PublicPreferences {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl galaxyui::SingletonEntity for PublicPreferences {}
|
||||
impl galaxyui_core::SingletonEntity for PublicPreferences {}
|
||||
|
||||
/// A newtype wrapper for the private preferences backend.
|
||||
///
|
||||
@@ -150,11 +131,11 @@ impl Deref for PrivatePreferences {
|
||||
}
|
||||
}
|
||||
|
||||
impl galaxyui::Entity for PrivatePreferences {
|
||||
impl galaxyui_core::Entity for PrivatePreferences {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl galaxyui::SingletonEntity for PrivatePreferences {}
|
||||
impl galaxyui_core::SingletonEntity for PrivatePreferences {}
|
||||
|
||||
/// An enum representing the different platforms a setting could apply to.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -204,7 +185,10 @@ impl SupportedPlatforms {
|
||||
cfg!(all(not(target_family = "wasm"), target_os = "macos"))
|
||||
}
|
||||
SupportedPlatforms::LINUX => {
|
||||
cfg!(all(not(target_family = "wasm"), target_os = "linux"))
|
||||
cfg!(all(
|
||||
not(target_family = "wasm"),
|
||||
any(target_os = "linux", target_os = "freebsd")
|
||||
))
|
||||
}
|
||||
SupportedPlatforms::WINDOWS => {
|
||||
cfg!(all(not(target_family = "wasm"), target_os = "windows"))
|
||||
@@ -370,7 +354,7 @@ pub trait Setting {
|
||||
fn set_value_from_cloud_sync(
|
||||
&mut self,
|
||||
new_value: Self::Value,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
/// Sets the value of the setting persisting it to storage.
|
||||
@@ -386,7 +370,7 @@ pub trait Setting {
|
||||
/// Sets the value of the setting to its default and persists it to storage.
|
||||
fn set_value_to_default(
|
||||
&mut self,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
self.set_value(Self::default_value(), ctx)
|
||||
}
|
||||
@@ -396,11 +380,11 @@ pub trait Setting {
|
||||
/// Private settings use the platform-native store; public settings use
|
||||
/// the main preferences backend (which may be the TOML settings file).
|
||||
fn preferences_for_setting(ctx: &AppContext) -> &dyn UserPreferences {
|
||||
use galaxyui::SingletonEntity;
|
||||
use galaxyui_core::SingletonEntity;
|
||||
|
||||
if Self::is_private() {
|
||||
<PrivatePreferences as SingletonEntity>::as_ref(ctx).deref()
|
||||
} else if is_settings_file_enabled() {
|
||||
} else if FeatureFlag::SettingsFile.is_enabled() {
|
||||
<PublicPreferences as SingletonEntity>::as_ref(ctx).as_preferences()
|
||||
} else {
|
||||
// When the settings file is disabled, fall back to the private
|
||||
@@ -573,6 +557,89 @@ pub trait Setting {
|
||||
fn is_value_explicitly_set(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Shared persistence operations for typed settings backed by secure storage.
|
||||
///
|
||||
/// Implementors remain responsible for routing their [`Setting`] lifecycle
|
||||
/// methods through this trait and for keeping the setting private and
|
||||
/// non-synced when the value must not be exposed through ordinary settings
|
||||
/// storage.
|
||||
pub trait SecureSetting: Setting {
|
||||
/// Writes this setting's serialized value through its selected secure-storage path.
|
||||
fn write_secure_storage_value(
|
||||
storage: &dyn secure_storage::SecureStorage,
|
||||
key: &str,
|
||||
value: &str,
|
||||
) -> Result<(), secure_storage::Error> {
|
||||
storage.write_value(key, value)
|
||||
}
|
||||
/// Reads and deserializes this setting from secure storage.
|
||||
///
|
||||
/// Missing, unreadable, or malformed values return `None`, allowing the
|
||||
/// setting to fail closed to its default value.
|
||||
fn read_from_secure_storage(ctx: &AppContext) -> Option<Self::Value> {
|
||||
let value = match ctx.secure_storage().read_value(Self::storage_key()) {
|
||||
Ok(value) => value,
|
||||
Err(secure_storage::Error::NotFound) => return None,
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Failed to read {} from secure storage: {err:#}",
|
||||
Self::setting_name()
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
match serde_json::from_str(&value) {
|
||||
Ok(value) => Some(value),
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Failed to deserialize {} from secure storage: {err:#}",
|
||||
Self::setting_name()
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Persists this setting to secure storage if its typed value changed.
|
||||
fn write_to_secure_storage(new_value: &Self::Value, ctx: &AppContext) -> Result<bool> {
|
||||
let stored_value_matches = match ctx.secure_storage().read_value(Self::storage_key()) {
|
||||
Ok(stored) => serde_json::from_str::<Self::Value>(&stored)
|
||||
.is_ok_and(|stored| stored == *new_value),
|
||||
Err(secure_storage::Error::NotFound) => false,
|
||||
Err(err) => {
|
||||
return Err(anyhow::anyhow!(err)).context(format!(
|
||||
"Failed to read existing {} from secure storage",
|
||||
Self::setting_name()
|
||||
));
|
||||
}
|
||||
};
|
||||
if stored_value_matches {
|
||||
return Ok(false);
|
||||
}
|
||||
let serialized = serde_json::to_string(new_value).context(format!(
|
||||
"Failed to serialize {} for secure storage",
|
||||
Self::setting_name()
|
||||
))?;
|
||||
Self::write_secure_storage_value(ctx.secure_storage(), Self::storage_key(), &serialized)
|
||||
.context(format!(
|
||||
"Failed to write {} to secure storage",
|
||||
Self::setting_name()
|
||||
))?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Removes this setting from secure storage.
|
||||
fn clear_from_secure_storage(ctx: &AppContext) -> Result<()> {
|
||||
match ctx.secure_storage().remove_value(Self::storage_key()) {
|
||||
Ok(()) | Err(secure_storage::Error::NotFound) => Ok(()),
|
||||
Err(err) => Err(anyhow::anyhow!(err)).context(format!(
|
||||
"Failed to clear {} from secure storage",
|
||||
Self::setting_name()
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for settings that can be toggled between two values.
|
||||
pub trait ToggleableSetting: Setting {
|
||||
/// Toggles the value of the setting and persists it to storage, returning
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
//! ```
|
||||
//!
|
||||
//! The macro also generates an `Event` type that is passed to subscribers of the
|
||||
//! setting group model. The name of the type is created by appended 'ChangedEvent'
|
||||
//! setting group model. The name of the type is created by appending 'ChangedEvent'
|
||||
//! to the name of the setting group:
|
||||
//!
|
||||
//! ```
|
||||
@@ -109,10 +109,11 @@
|
||||
//! Once you've defined a setting, usage is straightforward:
|
||||
//!
|
||||
//! ```
|
||||
//! # use galaxyui::*;
|
||||
//! # use settings::macros::*;
|
||||
//! # use settings::manager::SettingsManager;
|
||||
//! # use settings::*;
|
||||
//! # use galaxyui_core::prelude::*;
|
||||
//! # use galaxyui_core::{elements, App};
|
||||
//! # use galaxyui_extras::user_preferences;
|
||||
//! define_settings_group!(ExampleGroup, settings: [
|
||||
//! bool_setting: BoolSetting {
|
||||
@@ -312,7 +313,7 @@ macro_rules! define_setting {
|
||||
|
||||
fn clear_value(
|
||||
&mut self,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut $crate::galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
use $crate::ChangeEventReason;
|
||||
Self::clear_from_preferences(Self::preferences_for_setting(ctx))?;
|
||||
@@ -327,9 +328,8 @@ macro_rules! define_setting {
|
||||
fn set_value_from_cloud_sync(
|
||||
&mut self,
|
||||
new_value: Self::Value,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut $crate::galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
use $crate::ChangeEventReason;
|
||||
let changed_in_storage =
|
||||
Self::write_to_preferences(&new_value, Self::preferences_for_setting(ctx))?;
|
||||
if self.value() != &new_value || changed_in_storage {
|
||||
@@ -345,9 +345,8 @@ macro_rules! define_setting {
|
||||
fn set_value(
|
||||
&mut self,
|
||||
new_value: Self::Value,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut $crate::galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
use $crate::ChangeEventReason;
|
||||
let changed_in_storage =
|
||||
Self::write_to_preferences(&new_value, Self::preferences_for_setting(ctx))?;
|
||||
if self.value() != &new_value || changed_in_storage {
|
||||
@@ -364,9 +363,8 @@ macro_rules! define_setting {
|
||||
&mut self,
|
||||
new_value: Self::Value,
|
||||
explicitly_set: bool,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut $crate::galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
use $crate::ChangeEventReason;
|
||||
let validated = self.validate(new_value);
|
||||
if self.value() != &validated || self.is_explicitly_set != explicitly_set {
|
||||
self.inner = validated;
|
||||
@@ -582,9 +580,8 @@ macro_rules! implement_setting_for_enum {
|
||||
|
||||
fn clear_value(
|
||||
&mut self,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut $crate::galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
use $crate::ChangeEventReason;
|
||||
Self::clear_from_preferences(Self::preferences_for_setting(ctx))?;
|
||||
*self = self.validate(Self::default_value());
|
||||
ctx.emit($crate::macros::concat_idents!(EventName = $group, ChangedEvent { EventName::$name {
|
||||
@@ -596,9 +593,8 @@ macro_rules! implement_setting_for_enum {
|
||||
fn set_value_from_cloud_sync(
|
||||
&mut self,
|
||||
new_value: Self::Value,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut $crate::galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
use $crate::ChangeEventReason;
|
||||
let changed_in_storage =
|
||||
Self::write_to_preferences(&new_value, Self::preferences_for_setting(ctx))?;
|
||||
if self.value() != &new_value || changed_in_storage {
|
||||
@@ -613,9 +609,8 @@ macro_rules! implement_setting_for_enum {
|
||||
fn set_value(
|
||||
&mut self,
|
||||
new_value: Self::Value,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut $crate::galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
use $crate::ChangeEventReason;
|
||||
let changed_in_storage =
|
||||
Self::write_to_preferences(&new_value, Self::preferences_for_setting(ctx))?;
|
||||
if self.value() != &new_value || changed_in_storage {
|
||||
@@ -631,9 +626,8 @@ macro_rules! implement_setting_for_enum {
|
||||
&mut self,
|
||||
new_value: Self::Value,
|
||||
_explicitly_set: bool,
|
||||
ctx: &mut galaxyui::ModelContext<Self::Group>,
|
||||
ctx: &mut $crate::galaxyui_core::ModelContext<Self::Group>,
|
||||
) -> anyhow::Result<()> {
|
||||
use $crate::ChangeEventReason;
|
||||
let validated = self.validate(new_value);
|
||||
if self.value() != &validated {
|
||||
*self = validated;
|
||||
@@ -714,8 +708,7 @@ macro_rules! define_settings_group {
|
||||
|
||||
impl $group {
|
||||
#[allow(dead_code)]
|
||||
fn new_from_storage(ctx: &mut galaxyui::ModelContext<Self>) -> Self {
|
||||
use $crate::Setting;
|
||||
fn new_from_storage(ctx: &mut $crate::galaxyui_core::ModelContext<Self>) -> Self {
|
||||
Self {
|
||||
$(
|
||||
$var: <$setting>::new_from_storage(ctx),
|
||||
@@ -725,8 +718,7 @@ macro_rules! define_settings_group {
|
||||
|
||||
#[cfg(any(test, feature = "integration_tests"))]
|
||||
#[allow(dead_code)]
|
||||
pub fn new_with_defaults(_ctx: &mut galaxyui::ModelContext<Self>) -> Self {
|
||||
use $crate::Setting;
|
||||
pub fn new_with_defaults(_ctx: &mut $crate::galaxyui_core::ModelContext<Self>) -> Self {
|
||||
Self {
|
||||
$(
|
||||
$var: <$setting>::new(None),
|
||||
@@ -735,7 +727,7 @@ macro_rules! define_settings_group {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn register(ctx: &mut (impl galaxyui::GetSingletonModelHandle + galaxyui::AddSingletonModel + galaxyui::UpdateModel)) -> galaxyui::ModelHandle<Self> {
|
||||
pub fn register(ctx: &mut (impl $crate::galaxyui_core::GetSingletonModelHandle + $crate::galaxyui_core::AddSingletonModel + $crate::galaxyui_core::UpdateModel)) -> $crate::galaxyui_core::ModelHandle<Self> {
|
||||
let settings_group = ctx.add_singleton_model(|ctx| {
|
||||
Self::new_from_storage(ctx)
|
||||
});
|
||||
@@ -758,7 +750,6 @@ macro_rules! define_settings_group {
|
||||
/// If any of the settings in the setting group are supported, then the group is supported.
|
||||
/// If none of the settings in the group are supported, then the group is not supported.
|
||||
fn is_supported_on_current_platform(&self) -> bool {
|
||||
use $crate::Setting;
|
||||
$(
|
||||
if self.$var.is_supported_on_current_platform() {
|
||||
return true;
|
||||
@@ -769,24 +760,23 @@ macro_rules! define_settings_group {
|
||||
}
|
||||
|
||||
$crate::macros::concat_idents!(EventName = $group, ChangedEvent {
|
||||
use $crate::ChangeEventReason;
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum EventName {
|
||||
$(
|
||||
$setting {
|
||||
#[allow(dead_code)]
|
||||
change_event_reason: ChangeEventReason,
|
||||
change_event_reason: $crate::ChangeEventReason,
|
||||
},
|
||||
)*
|
||||
}
|
||||
|
||||
impl galaxyui::Entity for $group {
|
||||
impl $crate::galaxyui_core::Entity for $group {
|
||||
type Event = EventName;
|
||||
}
|
||||
});
|
||||
|
||||
impl galaxyui::SingletonEntity for $group {}
|
||||
impl $crate::galaxyui_core::SingletonEntity for $group {}
|
||||
};
|
||||
}
|
||||
pub use define_settings_group;
|
||||
@@ -816,21 +806,21 @@ macro_rules! generate_settings_event_fn {
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_snake_case)]
|
||||
fn fn_name(
|
||||
settings_group: galaxyui::ModelHandle<$group>,
|
||||
settings_group: $crate::galaxyui_core::ModelHandle<$group>,
|
||||
ctx: &mut (
|
||||
impl galaxyui::GetSingletonModelHandle
|
||||
+ galaxyui::AddSingletonModel
|
||||
+ galaxyui::UpdateModel
|
||||
impl $crate::galaxyui_core::GetSingletonModelHandle
|
||||
+ $crate::galaxyui_core::AddSingletonModel
|
||||
+ $crate::galaxyui_core::UpdateModel
|
||||
),
|
||||
) {
|
||||
use anyhow::anyhow;
|
||||
use galaxyui::SingletonEntity;
|
||||
use serde_json;
|
||||
use $crate::Setting as _;
|
||||
use $crate::manager::{SettingsEvent, SettingsManager};
|
||||
use $crate::galaxyui_core::SingletonEntity;
|
||||
SettingsManager::handle(ctx).update(ctx, |manager, ctx| {
|
||||
// Propagate per settings change events through the SettingsManager
|
||||
ctx.subscribe_to_model(&settings_group, |_manager, _, ctx| {
|
||||
ctx.subscribe_to_model(&settings_group, |_manager, _, _, ctx| {
|
||||
ctx.emit(SettingsEvent::LocalPreferencesUpdated {
|
||||
storage_key: $setting::storage_key().to_string(),
|
||||
sync_to_cloud: $setting::sync_to_cloud(),
|
||||
@@ -861,7 +851,6 @@ macro_rules! generate_settings_event_fn {
|
||||
$setting::max_table_depth(),
|
||||
$setting::is_private(),
|
||||
move |value, from_cloud_sync, ctx| {
|
||||
use $crate::_settings_value::SettingsValue as _;
|
||||
// Try SettingsValue first (handles snake_case enums etc.),
|
||||
// then fall back to serde for cloud sync values.
|
||||
let value = serde_json::from_str::<serde_json::Value>(&value)
|
||||
@@ -901,7 +890,6 @@ macro_rules! generate_settings_event_fn {
|
||||
})
|
||||
},
|
||||
move |value, explicitly_set, ctx| {
|
||||
use $crate::_settings_value::SettingsValue as _;
|
||||
let value = serde_json::from_str::<serde_json::Value>(&value)
|
||||
.ok()
|
||||
.and_then(|json_val| {
|
||||
@@ -919,7 +907,6 @@ macro_rules! generate_settings_event_fn {
|
||||
})
|
||||
},
|
||||
|left, right| {
|
||||
use $crate::_settings_value::SettingsValue as _;
|
||||
let parse =
|
||||
|s: &str| -> anyhow::Result<<$setting as $crate::Setting>::Value> {
|
||||
let json_val = serde_json::from_str::<serde_json::Value>(s)?;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use anyhow::Result;
|
||||
use galaxyui::{AppContext, SingletonEntity};
|
||||
use galaxyui_core::{AppContext, SingletonEntity};
|
||||
|
||||
use crate::manager::SettingsManager;
|
||||
use crate::{Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use crate::*;
|
||||
use crate::{Setting, SupportedPlatforms, SyncToCloud, *};
|
||||
|
||||
define_settings_group!(TestSettings, settings: [
|
||||
simple_setting: SimpleSetting {
|
||||
@@ -53,9 +51,9 @@ struct EventListener {
|
||||
}
|
||||
|
||||
impl EventListener {
|
||||
fn new(ctx: &mut galaxyui::ModelContext<Self>) -> Self {
|
||||
fn new(ctx: &mut galaxyui_core::ModelContext<Self>) -> Self {
|
||||
let test_settings = TestSettings::handle(ctx);
|
||||
ctx.subscribe_to_model(&test_settings, |me, event, _ctx| {
|
||||
ctx.subscribe_to_model(&test_settings, |me, _, event, _ctx| {
|
||||
// Update our internal state if we get a change event for
|
||||
// SimpleSetting.
|
||||
if matches!(event, TestSettingsChangedEvent::SimpleSetting { .. }) {
|
||||
@@ -67,7 +65,7 @@ impl EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
impl galaxyui::Entity for EventListener {
|
||||
impl galaxyui_core::Entity for EventListener {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
@@ -92,7 +90,7 @@ fn test_can_override_storage_key() {
|
||||
|
||||
#[test]
|
||||
fn test_set_value_raises_changed_event_no_save() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -124,7 +122,7 @@ fn test_set_value_raises_changed_event_no_save() {
|
||||
|
||||
#[test]
|
||||
fn test_set_value_raises_changed_event_save() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -156,7 +154,7 @@ fn test_set_value_raises_changed_event_save() {
|
||||
|
||||
#[test]
|
||||
fn test_save_and_load_lifecycle() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -195,7 +193,7 @@ fn test_save_and_load_lifecycle() {
|
||||
|
||||
#[test]
|
||||
fn test_toggleable_setting() -> Result<()> {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -254,7 +252,7 @@ fn test_explicit_value_tracking_with_some() {
|
||||
|
||||
#[test]
|
||||
fn test_explicit_value_tracking_after_set_value() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -284,7 +282,7 @@ fn test_explicit_value_tracking_after_set_value() {
|
||||
|
||||
#[test]
|
||||
fn test_explicit_value_tracking_after_clear_value() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -325,7 +323,7 @@ fn test_explicit_value_tracking_after_clear_value() {
|
||||
|
||||
#[test]
|
||||
fn test_explicit_value_tracking_from_storage() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -399,7 +397,7 @@ fn test_private_setting_storage_key_is_explicit_override() {
|
||||
|
||||
#[test]
|
||||
fn test_load_value_updates_value_without_persisting() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -430,7 +428,7 @@ fn test_load_value_updates_value_without_persisting() {
|
||||
|
||||
#[test]
|
||||
fn test_load_value_emits_event_on_change() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -461,7 +459,7 @@ fn test_load_value_emits_event_on_change() {
|
||||
|
||||
#[test]
|
||||
fn test_load_value_skips_event_when_unchanged() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -490,7 +488,7 @@ fn test_load_value_skips_event_when_unchanged() {
|
||||
|
||||
#[test]
|
||||
fn test_load_value_updates_explicitly_set_flag() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -526,7 +524,7 @@ fn test_load_value_updates_explicitly_set_flag() {
|
||||
|
||||
#[test]
|
||||
fn test_load_value_resets_explicitly_set_flag() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -569,7 +567,7 @@ fn test_load_value_resets_explicitly_set_flag() {
|
||||
|
||||
#[test]
|
||||
fn test_explicit_value_tracking_cloud_sync() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -627,11 +625,6 @@ mod file_transform_tests {
|
||||
// Private / public settings split tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Tests that call `set_settings_file_enabled` are marked `#[serial_test::serial]`
|
||||
// because they mutate the process-global `SETTINGS_FILE_ENABLED` AtomicBool and
|
||||
// would race under `cargo test` (thread-based parallelism). This can be removed
|
||||
// when the SettingsFile feature flag is cleaned up and the global flag is deleted.
|
||||
|
||||
#[test]
|
||||
fn test_is_private_returns_false_for_public_setting() {
|
||||
assert!(!SimpleSetting::is_private());
|
||||
@@ -643,19 +636,9 @@ fn test_is_private_returns_true_for_private_setting() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_settings_file_enabled_flag_round_trip() {
|
||||
crate::set_settings_file_enabled(true);
|
||||
assert!(crate::is_settings_file_enabled());
|
||||
crate::set_settings_file_enabled(false);
|
||||
assert!(!crate::is_settings_file_enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_public_setting_writes_to_public_prefs_when_flag_enabled() {
|
||||
crate::set_settings_file_enabled(true);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(true);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -690,10 +673,9 @@ fn test_public_setting_writes_to_public_prefs_when_flag_enabled() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_private_setting_writes_to_private_prefs_when_flag_enabled() {
|
||||
crate::set_settings_file_enabled(true);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(true);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -737,10 +719,9 @@ fn test_private_setting_writes_to_private_prefs_when_flag_enabled() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_new_from_storage_reads_from_correct_backend_when_flag_enabled() {
|
||||
crate::set_settings_file_enabled(true);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(true);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -775,10 +756,9 @@ fn test_new_from_storage_reads_from_correct_backend_when_flag_enabled() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_clear_value_clears_from_correct_backend() {
|
||||
crate::set_settings_file_enabled(true);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(true);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -831,10 +811,9 @@ fn test_clear_value_clears_from_correct_backend() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_public_setting_uses_private_prefs_when_flag_disabled() {
|
||||
crate::set_settings_file_enabled(false);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(false);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -872,10 +851,9 @@ fn test_public_setting_uses_private_prefs_when_flag_disabled() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_private_setting_uses_private_prefs_when_flag_disabled() {
|
||||
crate::set_settings_file_enabled(false);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(false);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -906,10 +884,9 @@ fn test_private_setting_uses_private_prefs_when_flag_disabled() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_new_from_storage_reads_from_private_backend_when_flag_disabled() {
|
||||
crate::set_settings_file_enabled(false);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(false);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -950,7 +927,7 @@ fn test_new_from_storage_reads_from_private_backend_when_flag_disabled() {
|
||||
|
||||
#[test]
|
||||
fn test_manager_is_private_for_storage_key() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -975,7 +952,7 @@ fn test_manager_is_private_for_storage_key() {
|
||||
|
||||
#[test]
|
||||
fn test_manager_default_values_for_settings_file_excludes_private() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -1003,10 +980,9 @@ fn test_manager_default_values_for_settings_file_excludes_private() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_manager_read_local_setting_value_routes_when_flag_enabled() {
|
||||
crate::set_settings_file_enabled(true);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(true);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -1051,10 +1027,9 @@ fn test_manager_read_local_setting_value_routes_when_flag_enabled() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_manager_read_local_setting_value_falls_back_when_flag_disabled() {
|
||||
crate::set_settings_file_enabled(false);
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(false);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
TestSettings::register(&mut app);
|
||||
@@ -1102,15 +1077,14 @@ fn test_manager_read_local_setting_value_falls_back_when_flag_disabled() {
|
||||
/// section like `[account]` are invisible to the SettingsManager and the
|
||||
/// cloud preferences syncer clobbers them with stale cloud state.
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_manager_read_local_setting_value_respects_hierarchy_with_settings_file() {
|
||||
use galaxyui_extras::user_preferences::toml_backed::TomlBackedUserPreferences;
|
||||
|
||||
crate::set_settings_file_enabled(true);
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(true);
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let file_path = dir.path().join("settings.toml");
|
||||
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
// Use the TOML-backed store for public preferences so the hierarchy
|
||||
// routing actually matters; in-memory preferences ignore hierarchy
|
||||
// entirely and would hide this bug.
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use galaxyui::{AppContext, Entity, ModelContext, SingletonEntity};
|
||||
use galaxy_features::FeatureFlag;
|
||||
use galaxyui_core::{AppContext, Entity, ModelContext, SingletonEntity};
|
||||
use galaxyui_extras::user_preferences::UserPreferences;
|
||||
|
||||
use super::PrivatePreferences;
|
||||
|
||||
use super::{RespectUserSyncSetting, SupportedPlatforms, SyncToCloud};
|
||||
use super::{PrivatePreferences, RespectUserSyncSetting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
type UpdateFn = Box<dyn FnMut(String, bool, &mut AppContext) -> Result<()>>;
|
||||
|
||||
@@ -94,7 +92,7 @@ pub enum SettingsEvent {
|
||||
}
|
||||
|
||||
impl SettingsManager {
|
||||
/// Registers a function that updates a a setting with the given storage key
|
||||
/// Registers a function that updates a setting with the given storage key
|
||||
/// to have a new value. Also tracks whether that storage key is for a cloud-synced
|
||||
/// setting and what platforms it's supported on.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -232,7 +230,7 @@ impl SettingsManager {
|
||||
<PrivatePreferences as SingletonEntity>::as_ref(ctx).deref();
|
||||
let prefs: &dyn UserPreferences = if self.is_private_for_storage_key(storage_key) {
|
||||
private
|
||||
} else if super::is_settings_file_enabled() {
|
||||
} else if FeatureFlag::SettingsFile.is_enabled() {
|
||||
<super::PublicPreferences as SingletonEntity>::as_ref(ctx).as_preferences()
|
||||
} else {
|
||||
// When the settings file is disabled, fall back to the private
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use galaxyui::SingletonEntity;
|
||||
use galaxyui_core::SingletonEntity;
|
||||
|
||||
use crate::manager::SettingsManager;
|
||||
use crate::{Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use crate::*;
|
||||
use crate::{Setting, SupportedPlatforms, SyncToCloud, *};
|
||||
|
||||
define_settings_group!(TestSettings, settings: [
|
||||
never_sync_setting: SimpleSetting {
|
||||
@@ -63,7 +61,7 @@ pub fn init_and_register_user_preferences(ctx: &mut AppContext) {
|
||||
|
||||
#[test]
|
||||
fn test_is_setting_syncable_on_current_platform() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_and_register_user_preferences);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
|
||||
@@ -155,12 +153,7 @@ fn test_is_setting_syncable_on_current_platform() {
|
||||
}
|
||||
|
||||
mod reload_all_public_settings_tests {
|
||||
use galaxyui::SingletonEntity;
|
||||
|
||||
use crate::manager::SettingsManager;
|
||||
use crate::{Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use crate::*;
|
||||
|
||||
define_settings_group!(ReloadTestSettings, settings: [
|
||||
public_flag: PublicFlag {
|
||||
@@ -197,7 +190,7 @@ mod reload_all_public_settings_tests {
|
||||
/// in the preferences backend.
|
||||
#[test]
|
||||
fn test_loads_present_keys() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_prefs);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
ReloadTestSettings::register(&mut app);
|
||||
@@ -231,7 +224,7 @@ mod reload_all_public_settings_tests {
|
||||
/// reload (the key-deletion scenario).
|
||||
#[test]
|
||||
fn test_resets_absent_keys_to_defaults() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_prefs);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
ReloadTestSettings::register(&mut app);
|
||||
@@ -272,7 +265,7 @@ mod reload_all_public_settings_tests {
|
||||
/// This is the property that prevents the infinite watcher loop.
|
||||
#[test]
|
||||
fn test_absent_keys_are_not_written_back() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_prefs);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
ReloadTestSettings::register(&mut app);
|
||||
@@ -313,7 +306,7 @@ mod reload_all_public_settings_tests {
|
||||
/// of settings that fail to deserialize (invalid value in file).
|
||||
#[test]
|
||||
fn test_reload_returns_failed_keys_for_invalid_values() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_prefs);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
ReloadTestSettings::register(&mut app);
|
||||
@@ -354,7 +347,7 @@ mod reload_all_public_settings_tests {
|
||||
/// when all values are valid.
|
||||
#[test]
|
||||
fn test_reload_returns_empty_vec_on_success() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
app.update(init_prefs);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
ReloadTestSettings::register(&mut app);
|
||||
@@ -384,8 +377,8 @@ mod reload_all_public_settings_tests {
|
||||
/// values without modifying in-memory state.
|
||||
#[test]
|
||||
fn test_validate_detects_invalid_values() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
crate::set_settings_file_enabled(true);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(true);
|
||||
app.update(init_prefs);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
ReloadTestSettings::register(&mut app);
|
||||
@@ -423,8 +416,8 @@ mod reload_all_public_settings_tests {
|
||||
/// stored values are valid.
|
||||
#[test]
|
||||
fn test_validate_returns_empty_when_all_valid() {
|
||||
galaxyui::App::test((), |mut app| async move {
|
||||
crate::set_settings_file_enabled(true);
|
||||
galaxyui_core::App::test((), |mut app| async move {
|
||||
let _guard = galaxy_features::FeatureFlag::SettingsFile.override_enabled(true);
|
||||
app.update(init_prefs);
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
ReloadTestSettings::register(&mut app);
|
||||
@@ -543,6 +536,7 @@ mod write_to_preferences_tests {
|
||||
use galaxyui_extras::user_preferences::toml_backed::TomlBackedUserPreferences;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
|
||||
)]
|
||||
@@ -604,7 +598,6 @@ mod write_to_preferences_tests {
|
||||
/// null-stripping and key-reordering happens.
|
||||
#[test]
|
||||
fn test_no_spurious_write_with_toml_backend() {
|
||||
use galaxyui_extras::user_preferences::toml_backed::TomlBackedUserPreferences;
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let file_path = dir.path().join("settings.toml");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Schema metadata for settings, used by the JSON Schema generator.
|
||||
|
||||
use schemars::Schema;
|
||||
use schemars::SchemaGenerator;
|
||||
use schemars::{Schema, SchemaGenerator};
|
||||
|
||||
use crate::SupportedPlatforms;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use schemars::SchemaGenerator;
|
||||
use schemars::{JsonSchema, SchemaGenerator};
|
||||
|
||||
use crate::schema::SettingSchemaEntry;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user