Skip to content

Commit 7370373

Browse files
committed
Update SettingsManager.cs to support key scoped settings
1 parent afca4cf commit 7370373

File tree

3 files changed

+41
-31
lines changed

3 files changed

+41
-31
lines changed

src/NexusMods.Backend/Settings/SettingsManager.cs

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ internal class SettingsManager : ISettingsManager
1515
private readonly IServiceProvider _serviceProvider;
1616
private readonly ILogger _logger;
1717

18-
private readonly Subject<(Type, object)> _subject = new();
19-
private readonly Dictionary<Type, object> _values = new();
18+
private readonly Subject<(SettingKey, object)> _subject = new();
19+
private readonly Dictionary<SettingKey, object> _values = new();
2020

2121
private readonly FrozenDictionary<Type, Func<object, object>> _overrides;
2222

@@ -78,37 +78,38 @@ public SettingsManager(IServiceProvider serviceProvider)
7878
_asyncStorageBackendMappings = asyncStorageBackendMappings.ToFrozenDictionary();
7979
}
8080

81-
private void CoreSet<T>(T value, bool notify) where T : class, ISettings, new()
81+
private void CoreSet<T>(T value, SettingKey settingKey, bool notify) where T : class, ISettings, new()
8282
{
83-
var type = typeof(T);
84-
_values[type] = value;
83+
_values[settingKey] = value;
8584
if (!notify) return;
8685

87-
_subject.OnNext((type, value));
88-
Save(value);
86+
_subject.OnNext((settingKey, value));
87+
Save(value, settingKey.Key);
8988
}
9089

91-
public void Set<T>(T value) where T : class, ISettings, new() => CoreSet(value, notify: true);
90+
public void Set<T>(T value, string? key) where T : class, ISettings, new() => CoreSet(value, new SettingKey(typeof(T), key), notify: true);
9291

93-
public T Get<T>() where T : class, ISettings, new()
92+
public T Get<T>(string? key) where T : class, ISettings, new()
9493
{
95-
if (_values.TryGetValue(typeof(T), out var obj))
94+
var settingKey = new SettingKey(typeof(T), key);
95+
96+
if (_values.TryGetValue(settingKey, out var obj))
9697
{
9798
Debug.Assert(obj is T);
9899
return (obj as T)!;
99100
}
100101

101-
var savedValue = Load<T>();
102+
var savedValue = Load<T>(key);
102103
if (savedValue is not null)
103104
{
104105
savedValue = Override(savedValue, out _);
105-
CoreSet(savedValue, notify: false);
106+
CoreSet(savedValue, settingKey, notify: false);
106107
return savedValue;
107108
}
108109

109110
var defaultValue = GetDefault<T>();
110111
defaultValue = Override(defaultValue, out var didOverride);
111-
CoreSet(defaultValue, notify: !didOverride);
112+
CoreSet(defaultValue, settingKey, notify: !didOverride);
112113

113114
return defaultValue;
114115

@@ -146,49 +147,51 @@ private object GetDefault(Type settingsType)
146147
return defaultValue;
147148
}
148149

149-
public T Update<T>(Func<T, T> updater) where T : class, ISettings, new()
150+
public T Update<T>(Func<T, T> updater, string? key) where T : class, ISettings, new()
150151
{
151-
var currentValue = Get<T>();
152+
var currentValue = Get<T>(key);
152153
var newValue = updater(currentValue);
153-
Set(newValue);
154+
Set(newValue, key);
154155

155156
return newValue;
156157
}
157158

158-
public Observable<T> GetChanges<T>(bool prependCurrent = false) where T : class, ISettings, new()
159+
public Observable<T> GetChanges<T>(string? key, bool prependCurrent = false) where T : class, ISettings, new()
159160
{
161+
var settingKey = new SettingKey(typeof(T), key);
162+
160163
var result = _subject
161-
.Where(tuple => tuple.Item1 == typeof(T))
164+
.Where(settingKey, static (tuple, state) => tuple.Item1 == state)
162165
.Select(tuple => (tuple.Item2 as T)!);
163166

164-
return prependCurrent ? result.Prepend(Get<T>()) : result;
167+
return prependCurrent ? result.Prepend(Get<T>(key)) : result;
165168
}
166169

167170
#region Save/Load
168171

169-
private void Save<T>(T value) where T : class, ISettings, new()
172+
private void Save<T>(T value, string? key) where T : class, ISettings, new()
170173
{
171174
var type = typeof(T);
172175

173176
if (_storageBackendMappings.TryGetValue(type, out var storageBackend))
174177
{
175178
try
176179
{
177-
storageBackend.Save(value);
180+
storageBackend.Save(value, key);
178181
}
179182
catch (Exception e)
180183
{
181184
_logger.LogError(e, "Exception while saving settings type `{Type}` with storage backend `{StorageBackendType}`", type, storageBackend.GetType());
182185
}
183186
} else if (_asyncStorageBackendMappings.TryGetValue(type, out var asyncStorageBackend))
184187
{
185-
Scheduler.ScheduleAsync((value, asyncStorageBackend, _logger), TimeSpan.Zero, async static (_, state, cancellationToken) =>
188+
Scheduler.ScheduleAsync((value, key, asyncStorageBackend, _logger), TimeSpan.Zero, async static (_, state, cancellationToken) =>
186189
{
187-
var (valueToSave, backend, logger) = state;
190+
var (valueToSave, stringKey, backend, logger) = state;
188191

189192
try
190193
{
191-
await backend.Save(valueToSave, cancellationToken);
194+
await backend.Save(valueToSave, stringKey, cancellationToken);
192195
}
193196
catch (Exception e)
194197
{
@@ -198,15 +201,15 @@ private object GetDefault(Type settingsType)
198201
}
199202
}
200203

201-
private T? Load<T>() where T : class, ISettings, new()
204+
private T? Load<T>(string? key) where T : class, ISettings, new()
202205
{
203206
var type = typeof(T);
204207

205208
if (_storageBackendMappings.TryGetValue(type, out var storageBackend))
206209
{
207210
try
208211
{
209-
return storageBackend.Load<T>();
212+
return storageBackend.Load<T>(key);
210213
}
211214
catch (Exception e)
212215
{
@@ -223,7 +226,7 @@ private object GetDefault(Type settingsType)
223226
{
224227
try
225228
{
226-
res = await asyncStorageBackend.Load<T>(cts.Token);
229+
res = await asyncStorageBackend.Load<T>(key, cts.Token);
227230
waitHandle.Set();
228231
}
229232
catch (Exception e)

src/NexusMods.Sdk/Settings/ISettingsManager.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ public interface ISettingsManager
1313
/// <summary>
1414
/// Sets the current value of <typeparamref name="T"/>.
1515
/// </summary>
16-
void Set<T>(T value) where T : class, ISettings, new();
16+
void Set<T>(T value, string? key = null) where T : class, ISettings, new();
1717

1818
/// <summary>
1919
/// Gets the current value for <typeparamref name="T"/>.
2020
/// </summary>
2121
/// <returns>The current value.</returns>
22-
T Get<T>() where T : class, ISettings, new();
22+
T Get<T>(string? key = null) where T : class, ISettings, new();
2323

2424
/// <summary>
2525
/// Gets the default value for <typeparamref name="T"/>.
@@ -35,12 +35,12 @@ public interface ISettingsManager
3535
/// value will be ignored, unless the modified input value gets returned.
3636
/// </param>
3737
/// <returns>The updated value.</returns>
38-
T Update<T>(Func<T, T> updater) where T : class, ISettings, new();
38+
T Update<T>(Func<T, T> updater, string? key = null) where T : class, ISettings, new();
3939

4040
/// <summary>
4141
/// Gets an observable stream to be notified about changes to <typeparamref name="T"/>.
4242
/// </summary>
43-
Observable<T> GetChanges<T>(bool prependCurrent) where T : class, ISettings, new();
43+
Observable<T> GetChanges<T>(string? key = null, bool prependCurrent = false) where T : class, ISettings, new();
4444

4545
/// <summary>
4646
/// Gets configs for all registered settings.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace NexusMods.Sdk.Settings;
2+
3+
4+
/// <summary>
5+
/// Identifies a setting by its type and an optional scope key.
6+
/// </summary>
7+
public readonly record struct SettingKey(Type SettingType, string? Key);

0 commit comments

Comments
 (0)