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
resx
bak
tmp
directx
mvvm
dlg
@ -85,4 +87,4 @@ searchplugin
Noresult
wpftk
mkv
flac
flac

View file

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

View file

@ -1,4 +1,5 @@
using System;
#nullable enable
using System;
using System.Globalization;
using System.IO;
using System.Text.Json;
@ -11,62 +12,73 @@ namespace Flow.Launcher.Infrastructure.Storage
/// </summary>
public class JsonStorage<T> where T : new()
{
protected T _data;
protected T? Data;
// need a new directory name
public const string DirectoryName = "Settings";
public const string FileSuffix = ".json";
public string FilePath { get; set; }
public string DirectoryPath { get; set; }
protected string FilePath { get; init; } = null!;
private string TempFilePath => $"{FilePath}.tmp";
private string BackupFilePath => $"{FilePath}.bak";
protected string DirectoryPath { get; init; } = null!;
public T Load()
{
string? serialized = null;
if (File.Exists(FilePath))
{
var serialized = File.ReadAllText(FilePath);
if (!string.IsNullOrWhiteSpace(serialized))
serialized = File.ReadAllText(FilePath);
}
if (!string.IsNullOrEmpty(serialized))
{
try
{
Deserialize(serialized);
Data = JsonSerializer.Deserialize<T>(serialized)?? TryLoadBackup() ?? LoadDefault();
}
else
catch (JsonException)
{
LoadDefault();
Data = TryLoadBackup() ?? LoadDefault();
}
}
else
{
LoadDefault();
Data = TryLoadBackup() ?? LoadDefault();
}
return _data.NonNull();
return Data.NonNull();
}
private void Deserialize(string serialized)
{
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()
private T LoadDefault()
{
if (File.Exists(FilePath))
{
BackupOriginFile();
}
_data = new T();
Save();
return new T();
}
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()
@ -82,13 +94,14 @@ namespace Flow.Launcher.Infrastructure.Storage
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()
{
_data = data;
Data = data;
}
}
}