Bugfix: initial bottom counts can throw error when a book was moved since Libation was last run

This commit is contained in:
Robert McRackan 2019-11-27 16:57:35 -05:00
parent 0683e5f55b
commit b1b426427c
4 changed files with 55 additions and 74 deletions

View file

@ -2,11 +2,12 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dinah.Core.Collections.Immutable;
using Newtonsoft.Json;
namespace FileManager
{
public static class FilePathCache
public static class FilePathCache
{
internal class CacheEntry
{
@ -15,22 +16,25 @@ namespace FileManager
public string Path { get; set; }
}
static List<CacheEntry> inMemoryCache { get; } = new List<CacheEntry>();
static Cache<CacheEntry> cache { get; } = new Cache<CacheEntry>();
public static string JsonFile => Path.Combine(Configuration.Instance.LibationFiles, "FilePaths.json");
static FilePathCache()
{
// load json into memory. if file doesn't exist, nothing to do. save() will create if needed
if (FileUtility.FileExists(JsonFile))
inMemoryCache = JsonConvert.DeserializeObject<List<CacheEntry>>(File.ReadAllText(JsonFile));
// load json into memory. if file doesn't exist, nothing to do. save() will create if needed
if (FileUtility.FileExists(JsonFile))
{
var list = JsonConvert.DeserializeObject<List<CacheEntry>>(File.ReadAllText(JsonFile));
cache = new Cache<CacheEntry>(list);
}
}
public static bool Exists(string id, FileType type) => GetPath(id, type) != null;
public static string GetPath(string id, FileType type)
{
var entry = inMemoryCache.SingleOrDefault(i => i.Id == id && i.FileType == type);
var entry = cache.SingleOrDefault(i => i.Id == id && i.FileType == type);
if (entry == null)
return null;
@ -44,51 +48,47 @@ namespace FileManager
return entry.Path;
}
private static object locker { get; } = new object();
private static void remove(CacheEntry entry)
{
lock (locker)
{
inMemoryCache.Remove(entry);
save();
}
}
{
cache.Remove(entry);
save();
}
public static void Upsert(string id, FileType type, string path)
{
if (!FileUtility.FileExists(path))
throw new FileNotFoundException("Cannot add path to cache. File not found");
lock (locker)
{
var entry = inMemoryCache.SingleOrDefault(i => i.Id == id && i.FileType == type);
if (entry != null)
entry.Path = path;
else
{
entry = new CacheEntry { Id = id, FileType = type, Path = path };
inMemoryCache.Add(entry);
}
save();
}
}
var entry = cache.SingleOrDefault(i => i.Id == id && i.FileType == type);
// ONLY call this within lock()
private static void save()
{
// create json if not exists
void resave() => File.WriteAllText(JsonFile, JsonConvert.SerializeObject(inMemoryCache, Formatting.Indented));
try { resave(); }
catch (IOException)
{
try { resave(); }
catch (IOException ex)
if (entry is null)
cache.Add(new CacheEntry { Id = id, FileType = type, Path = path });
else
entry.Path = path;
save();
}
// cache is thread-safe and lock free. but file saving is not
private static object locker { get; } = new object();
private static void save()
{
// create json if not exists
static void resave() => File.WriteAllText(JsonFile, JsonConvert.SerializeObject(cache.ToList(), Formatting.Indented));
lock (locker)
{
try { resave(); }
catch (IOException)
{
Serilog.Log.Logger.Error(ex, "Error saving FilePaths.json");
throw;
}
}
try { resave(); }
catch (IOException ex)
{
Serilog.Log.Logger.Error(ex, "Error saving FilePaths.json");
throw;
}
}
}
}
}
}