Skip to content
This repository was archived by the owner on Nov 24, 2022. It is now read-only.

Commit 716037d

Browse files
committed
Add support for immutable settings
1 parent 2bd8169 commit 716037d

31 files changed

Lines changed: 365 additions & 303 deletions

TabletBot.Common/AppData.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace TabletBot.Common
5+
{
6+
public static class AppData
7+
{
8+
private const string SETTINGS_FILENAME = "settings.json";
9+
private const string STATE_FILENAME = "state.json";
10+
11+
public static Settings Settings => ImmutableSettings ?? MutableSettings ?? new Settings();
12+
public static Settings? ImmutableSettings => ImmutableSettingsFile == null ? null : Read<Settings>(ImmutableSettingsFile);
13+
public static Settings? MutableSettings => Read<Settings>(UserSettingsFile);
14+
public static State State => Read<State>(StateFile) ?? new State();
15+
16+
public static FileInfo SettingsFile => ImmutableSettingsFile ?? UserSettingsFile;
17+
private static FileInfo? ImmutableSettingsFile => string.IsNullOrEmpty(ImmutableSettingsPath) ? null : new FileInfo(ImmutableSettingsPath);
18+
private static FileInfo UserSettingsFile => new FileInfo(MutableSettingsPath);
19+
public static FileInfo StateFile => new FileInfo(StatePath);
20+
21+
private static string? ImmutableSettingsPath => Environment.GetEnvironmentVariable("TABLETBOT_SETTINGS");
22+
private static string MutableSettingsPath => Path.Join(GetLocalAppdata(), SETTINGS_FILENAME);
23+
private static string StatePath => Path.Join(GetLocalAppdata(), STATE_FILENAME);
24+
25+
private static string GetLocalAppdata()
26+
{
27+
if (Environment.GetEnvironmentVariable("TABLETBOT_DATA") is string appdata)
28+
{
29+
return appdata;
30+
}
31+
if (OperatingSystem.IsWindows())
32+
{
33+
var localappdata = Environment.GetEnvironmentVariable("LOCALAPPDATA");
34+
return Path.Join(localappdata, "TabletBot");
35+
}
36+
if (OperatingSystem.IsLinux())
37+
{
38+
var home = Environment.GetEnvironmentVariable("HOME");
39+
return Path.Join(home, ".config", "TabletBot");
40+
}
41+
if (OperatingSystem.IsMacOS())
42+
{
43+
var macHome = Environment.GetEnvironmentVariable("HOME");
44+
return Path.Join(macHome, "Library", "Application Support", "TabletBot");
45+
}
46+
47+
throw new PlatformNotSupportedException("This platform is unsupported.");
48+
}
49+
50+
private static T? Read<T>(FileInfo file)
51+
{
52+
return file.Exists ? Serialization.Deserialize<T>(file) : default;
53+
}
54+
}
55+
}

TabletBot.Common/GuildSettings.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace TabletBot.Common
2+
{
3+
public sealed class GuildSettings
4+
{
5+
6+
}
7+
}

TabletBot.Common/Log.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
using System;
2-
using System.Threading.Tasks;
32

43
namespace TabletBot.Common
54
{
65
public static class Log
76
{
8-
public static event Action<LogMessage> Output;
7+
public static event Action<LogMessage>? Output;
98

109
public static void Write(string group, string text, LogLevel level = LogLevel.Info)
1110
{

TabletBot.Common/Platform.cs

Lines changed: 0 additions & 41 deletions
This file was deleted.

TabletBot.Common/Reflection/Extensions.cs

Lines changed: 0 additions & 14 deletions
This file was deleted.

TabletBot.Common/Serializable.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System.IO;
2+
using JetBrains.Annotations;
3+
using Newtonsoft.Json;
4+
5+
namespace TabletBot.Common
6+
{
7+
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.WithMembers)]
8+
public abstract class Serializable
9+
{
10+
[JsonIgnore]
11+
public abstract FileInfo File { get; }
12+
13+
[JsonIgnore]
14+
public bool Mutable
15+
{
16+
get
17+
{
18+
if (File.Exists)
19+
return (File.Attributes & FileAttributes.ReadOnly) == 0;
20+
21+
var dir = File.Directory;
22+
while (dir is { Exists: false })
23+
dir = dir.Parent;
24+
25+
if (dir == null)
26+
return false;
27+
28+
return (dir.Attributes & FileAttributes.ReadOnly) == 0;
29+
}
30+
}
31+
32+
public void Write()
33+
{
34+
if (!Mutable)
35+
{
36+
Log.Write("IO", $"Unable to write to '{File.FullName}'.", LogLevel.Error);
37+
return;
38+
}
39+
40+
if (File.Directory is { Exists: false })
41+
File.Directory.Create();
42+
43+
using (var fs = File.Create())
44+
Serialization.Serialize(fs, this);
45+
}
46+
47+
public override string ToString()
48+
{
49+
return JsonConvert.SerializeObject(this, Formatting.Indented);
50+
}
51+
}
52+
}

TabletBot.Common/Serialization.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System.IO;
2+
using JetBrains.Annotations;
3+
using Newtonsoft.Json;
4+
5+
namespace TabletBot.Common
6+
{
7+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8+
public class Serialization
9+
{
10+
private static readonly JsonSerializer Serializer = new JsonSerializer
11+
{
12+
Formatting = Formatting.Indented
13+
};
14+
15+
public static T Deserialize<T>(FileInfo file)
16+
{
17+
using (var fs = file.OpenRead())
18+
return Deserialize<T>(fs);
19+
}
20+
21+
public static T Deserialize<T>(Stream stream)
22+
{
23+
using (var sr = new StreamReader(stream))
24+
using (var jr = new JsonTextReader(sr))
25+
return Deserialize<T>(jr);
26+
}
27+
28+
public static T Deserialize<T>(JsonTextReader textReader)
29+
{
30+
return Serializer.Deserialize<T>(textReader)!;
31+
}
32+
33+
public static void Serialize(FileInfo file, object value)
34+
{
35+
if (file.Exists)
36+
file.Delete();
37+
38+
using (var fs = file.Create())
39+
Serialize(fs, value);
40+
}
41+
42+
public static void Serialize(Stream stream, object value)
43+
{
44+
using (var sw = new StreamWriter(stream))
45+
using (var jw = new JsonTextWriter(sw))
46+
Serialize(jw, value);
47+
}
48+
49+
public static void Serialize(JsonTextWriter textWriter, object value)
50+
{
51+
Serializer.Serialize(textWriter, value);
52+
}
53+
}
54+
}

TabletBot.Common/Settings.cs

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
using System.Collections.ObjectModel;
21
using System.IO;
3-
using System.Text.Json;
4-
using System.Text.Json.Serialization;
5-
using System.Threading.Tasks;
6-
using TabletBot.Common.Store;
72

83
namespace TabletBot.Common
94
{
10-
public sealed class Settings
5+
public sealed class Settings : Serializable
116
{
127
private const ulong MAIN_GUILD_ID = 615607687467761684;
138
private const ulong LOG_MESSAGE_CHANNEL_ID = 715344685853442198;
@@ -26,47 +21,6 @@ public sealed class Settings
2621

2722
public LogLevel LogLevel { set; get; } = LogLevel.Debug;
2823

29-
public Collection<RoleManagementMessageStore> ReactiveRoles { set; get; } = new Collection<RoleManagementMessageStore>();
30-
public Collection<SnippetStore> Snippets { set; get; } = new Collection<SnippetStore>();
31-
32-
[JsonIgnore]
33-
public bool RunAsUnit { set; get; } = false;
34-
35-
private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions
36-
{
37-
WriteIndented = true
38-
};
39-
40-
public async Task Write(FileInfo file)
41-
{
42-
if (file.Directory is { Exists: false })
43-
file.Directory.Create();
44-
await using (var fs = file.Create())
45-
await JsonSerializer.SerializeAsync(fs, this, SerializerOptions);
46-
}
47-
48-
public static async Task<Settings> Read(FileInfo file)
49-
{
50-
await using (var fs = file.OpenRead())
51-
return await JsonSerializer.DeserializeAsync<Settings>(fs);
52-
}
53-
54-
public async Task<string> ExportAsync()
55-
{
56-
await using (var ms = new MemoryStream())
57-
{
58-
await JsonSerializer.SerializeAsync(ms, this, SerializerOptions);
59-
ms.Position = 0;
60-
using (var sr = new StreamReader(ms))
61-
return await sr.ReadToEndAsync();
62-
}
63-
}
64-
65-
public async Task Overwrite()
66-
{
67-
Platform.SettingsFile.Refresh();
68-
if (Platform.SettingsFile.Exists)
69-
await Write(Platform.SettingsFile);
70-
}
24+
public override FileInfo File { get; } = AppData.SettingsFile;
7125
}
7226
}

TabletBot.Common/State.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Collections.ObjectModel;
2+
using System.IO;
3+
using Newtonsoft.Json;
4+
using TabletBot.Common.Store;
5+
6+
namespace TabletBot.Common
7+
{
8+
public sealed class State : Serializable
9+
{
10+
public Collection<RoleManagementMessage> ReactiveRoles { set; get; } = new Collection<RoleManagementMessage>();
11+
public Collection<Snippet> Snippets { set; get; } = new Collection<Snippet>();
12+
13+
[JsonIgnore]
14+
public bool RunAsUnit { set; get; }
15+
16+
public override FileInfo File { get; } = AppData.StateFile;
17+
}
18+
}

TabletBot.Common/Store/RoleManagementMessageStore.cs renamed to TabletBot.Common/Store/RoleManagementMessage.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1+
using JetBrains.Annotations;
2+
13
namespace TabletBot.Common.Store
24
{
3-
public class RoleManagementMessageStore
5+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
6+
public class RoleManagementMessage
47
{
5-
public RoleManagementMessageStore()
6-
{
7-
}
8-
9-
public RoleManagementMessageStore(ulong messageId, ulong targetRole, string emote)
8+
public RoleManagementMessage(ulong messageId, ulong targetRole, string emote)
109
{
1110
MessageId = messageId;
1211
RoleId = targetRole;

0 commit comments

Comments
 (0)