Merge pull request #1763 from Flow-Launcher/atomic_setting_save

Atomic Save for Setting
This commit is contained in:
Jeremy Wu 2023-01-23 10:00:32 +11:00 committed by GitHub
commit 3a3da51d92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 42 deletions

View file

@ -62,6 +62,8 @@ TobiasSekan
Img Img
img img
resx resx
bak
tmp
directx directx
mvvm mvvm
dlg dlg
@ -85,4 +87,4 @@ searchplugin
Noresult Noresult
wpftk wpftk
mkv mkv
flac flac

View file

@ -1,4 +1,6 @@
using System; #nullable enable
using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.Json; using System.Text.Json;
@ -16,7 +18,7 @@ namespace Flow.Launcher.Infrastructure
/// <summary> /// <summary>
/// http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy /// http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy
/// </summary> /// </summary>
public static T NonNull<T>(this T obj) public static T NonNull<T>(this T? obj)
{ {
if (obj == null) if (obj == null)
{ {

View file

@ -1,4 +1,5 @@
using System; #nullable enable
using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
@ -11,62 +12,73 @@ namespace Flow.Launcher.Infrastructure.Storage
/// </summary> /// </summary>
public class JsonStorage<T> where T : new() public class JsonStorage<T> where T : new()
{ {
protected T _data; protected T? Data;
// need a new directory name // need a new directory name
public const string DirectoryName = "Settings"; public const string DirectoryName = "Settings";
public const string FileSuffix = ".json"; public const string FileSuffix = ".json";
public string FilePath { get; set; } protected string FilePath { get; init; } = null!;
public string DirectoryPath { get; set; } private string TempFilePath => $"{FilePath}.tmp";
private string BackupFilePath => $"{FilePath}.bak";
protected string DirectoryPath { get; init; } = null!;
public T Load() public T Load()
{ {
string? serialized = null;
if (File.Exists(FilePath)) if (File.Exists(FilePath))
{ {
var serialized = File.ReadAllText(FilePath); serialized = File.ReadAllText(FilePath);
if (!string.IsNullOrWhiteSpace(serialized)) }
if (!string.IsNullOrEmpty(serialized))
{
try
{ {
Deserialize(serialized); Data = JsonSerializer.Deserialize<T>(serialized)?? TryLoadBackup() ?? LoadDefault();
} }
else catch (JsonException)
{ {
LoadDefault(); Data = TryLoadBackup() ?? LoadDefault();
} }
} }
else else
{ {
LoadDefault(); Data = TryLoadBackup() ?? LoadDefault();
} }
return _data.NonNull(); return Data.NonNull();
} }
private void Deserialize(string serialized) private T LoadDefault()
{
try
{
_data = JsonSerializer.Deserialize<T>(serialized);
}
catch (JsonException e)
{
LoadDefault();
Log.Exception($"|JsonStorage.Deserialize|Deserialize error for json <{FilePath}>", e);
}
if (_data == null)
{
LoadDefault();
}
}
private void LoadDefault()
{ {
if (File.Exists(FilePath)) if (File.Exists(FilePath))
{ {
BackupOriginFile(); BackupOriginFile();
} }
_data = new T(); return new T();
Save(); }
private T? TryLoadBackup()
{
if (!File.Exists(BackupFilePath))
return default;
try
{
var data = JsonSerializer.Deserialize<T>(File.ReadAllText(BackupFilePath));
if (data != null)
{
Log.Info($"|JsonStorage.Load|Failed to load settings.json, {BackupFilePath} restored successfully");
File.Replace(BackupFilePath, FilePath, null);
return data;
}
return default;
}
catch (JsonException)
{
return default;
}
} }
private void BackupOriginFile() private void BackupOriginFile()
@ -82,13 +94,14 @@ namespace Flow.Launcher.Infrastructure.Storage
public void Save() public void Save()
{ {
string serialized = JsonSerializer.Serialize(_data, new JsonSerializerOptions() { WriteIndented = true }); string serialized = JsonSerializer.Serialize(Data, new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(FilePath, serialized); File.WriteAllText(TempFilePath, serialized);
File.Replace(TempFilePath, FilePath, BackupFilePath);
File.Delete(TempFilePath);
} }
} }
[Obsolete("Deprecated as of Flow Launcher v1.8.0, on 2021.06.21. " +
"This is used only for Everything plugin v1.4.9 or below backwards compatibility")]
public class JsonStrorage<T> : JsonStorage<T> where T : new() { }
} }

View file

@ -18,7 +18,7 @@ namespace Flow.Launcher.Infrastructure.Storage
public PluginJsonStorage(T data) : this() public PluginJsonStorage(T data) : this()
{ {
_data = data; Data = data;
} }
} }
} }