- introduce `LyricFlow.Core.Backend` with shared DTOs, rhyme/spellcheck engines, and REST endpoints - wire Python GUI/core to run and call the backend via new bridge/client modules - add backend parity/integration tests and update packaging/ignore settings
156 lines
4.5 KiB
C#
156 lines
4.5 KiB
C#
using System.Text.Json;
|
|
using LyricFlow.Core.Dtos;
|
|
using LyricFlow.Core.Serialization;
|
|
|
|
namespace LyricFlow.Core.Services;
|
|
|
|
public class ProjectStateService
|
|
{
|
|
// MARK: - Project Read And Write
|
|
#region Project Read And Write
|
|
|
|
public ProjectStateDto ReadProject(string projectFile)
|
|
{
|
|
var fullPath = Path.GetFullPath(projectFile);
|
|
using var stream = File.OpenRead(fullPath);
|
|
using var document = JsonDocument.Parse(stream);
|
|
var fallbackName = Path.GetFileName(Path.GetDirectoryName(fullPath)) ?? string.Empty;
|
|
return FromRaw(document.RootElement, fallbackName);
|
|
}
|
|
|
|
public void WriteProject(string projectFile, ProjectStateDto state)
|
|
{
|
|
var fullPath = Path.GetFullPath(projectFile);
|
|
var directory = Path.GetDirectoryName(fullPath);
|
|
if (!string.IsNullOrWhiteSpace(directory))
|
|
{
|
|
Directory.CreateDirectory(directory);
|
|
}
|
|
|
|
File.WriteAllText(fullPath, JsonSerializer.Serialize(state, LyricFlowCoreJsonContext.Default.ProjectStateDto));
|
|
}
|
|
|
|
#endregion
|
|
|
|
// MARK: - Payload Conversion
|
|
#region Payload Conversion
|
|
|
|
private static ProjectStateDto FromRaw(JsonElement payload, string fallbackName)
|
|
{
|
|
if (payload.ValueKind != JsonValueKind.Object)
|
|
{
|
|
return new ProjectStateDto(2, fallbackName, [], null, [], false);
|
|
}
|
|
|
|
var version = ReadInt(payload, "version", 2);
|
|
var name = ReadString(payload, "name") ?? fallbackName;
|
|
var openFiles = ReadStringList(payload, "open_files");
|
|
var activeFile = ReadString(payload, "active_file");
|
|
var scratchpadOpen = ReadBool(payload, "scratchpad_open", false);
|
|
var cursorPositions = ReadCursorPositions(payload);
|
|
|
|
return new ProjectStateDto(Math.Max(1, version), name, openFiles, activeFile, cursorPositions, scratchpadOpen);
|
|
}
|
|
|
|
#endregion
|
|
|
|
// MARK: - JSON Readers
|
|
#region JSON Readers
|
|
|
|
private static Dictionary<string, int> ReadCursorPositions(JsonElement payload)
|
|
{
|
|
var cursorPositions = new Dictionary<string, int>();
|
|
if (!payload.TryGetProperty("cursor_positions", out var cursorElement) || cursorElement.ValueKind != JsonValueKind.Object)
|
|
{
|
|
return cursorPositions;
|
|
}
|
|
|
|
foreach (var property in cursorElement.EnumerateObject())
|
|
{
|
|
if (TryReadInt(property.Value, out var value))
|
|
{
|
|
cursorPositions[property.Name] = Math.Max(0, value);
|
|
}
|
|
}
|
|
|
|
return cursorPositions;
|
|
}
|
|
|
|
private static List<string> ReadStringList(JsonElement payload, string key)
|
|
{
|
|
var values = new List<string>();
|
|
if (!payload.TryGetProperty(key, out var element) || element.ValueKind != JsonValueKind.Array)
|
|
{
|
|
return values;
|
|
}
|
|
|
|
foreach (var item in element.EnumerateArray())
|
|
{
|
|
if (item.ValueKind == JsonValueKind.String)
|
|
{
|
|
var text = item.GetString();
|
|
if (!string.IsNullOrWhiteSpace(text))
|
|
{
|
|
values.Add(text);
|
|
}
|
|
}
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
private static string? ReadString(JsonElement payload, string key)
|
|
{
|
|
if (payload.TryGetProperty(key, out var element) && element.ValueKind == JsonValueKind.String)
|
|
{
|
|
return element.GetString();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static bool ReadBool(JsonElement payload, string key, bool fallback)
|
|
{
|
|
if (!payload.TryGetProperty(key, out var element))
|
|
{
|
|
return fallback;
|
|
}
|
|
|
|
return element.ValueKind switch
|
|
{
|
|
JsonValueKind.True => true,
|
|
JsonValueKind.False => false,
|
|
JsonValueKind.String when bool.TryParse(element.GetString(), out var value) => value,
|
|
_ => fallback,
|
|
};
|
|
}
|
|
|
|
private static int ReadInt(JsonElement payload, string key, int fallback)
|
|
{
|
|
if (payload.TryGetProperty(key, out var element) && TryReadInt(element, out var value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
|
|
private static bool TryReadInt(JsonElement element, out int value)
|
|
{
|
|
if (element.ValueKind == JsonValueKind.Number && element.TryGetInt32(out value))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (element.ValueKind == JsonValueKind.String && int.TryParse(element.GetString(), out value))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
value = 0;
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
}
|