stan44 e0f298ba36 Add LyricFlow .NET backend API and Python bridge integration
- 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
2026-03-15 01:44:56 -05:00

194 lines
5.6 KiB
C#

using LyricFlow.Core.Dtos;
namespace LyricFlow.Core.Services;
public class FileService
{
// MARK: - File Read And Write
#region File Read And Write
public FileReadResultDto ReadFile(string path)
{
try
{
var fullPath = Path.GetFullPath(path);
return new FileReadResultDto(true, $"Loaded {Path.GetFileName(fullPath)}", File.ReadAllText(fullPath), fullPath);
}
catch (Exception ex)
{
return new FileReadResultDto(false, ex.Message, null, null);
}
}
public FileWriteResultDto WriteFile(string path, string content)
{
try
{
var fullPath = Path.GetFullPath(path);
var directory = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrWhiteSpace(directory))
{
Directory.CreateDirectory(directory);
}
File.WriteAllText(fullPath, content);
return new FileWriteResultDto(true, $"Saved to {Path.GetFileName(fullPath)}", fullPath);
}
catch (Exception ex)
{
return new FileWriteResultDto(false, ex.Message, null);
}
}
#endregion
// MARK: - File System Mutations
#region File System Mutations
public FileWriteResultDto CreateFile(string path)
{
try
{
var fullPath = Path.GetFullPath(path);
var directory = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrWhiteSpace(directory))
{
Directory.CreateDirectory(directory);
}
using (File.Create(fullPath))
{
}
return new FileWriteResultDto(true, $"Created {Path.GetFileName(fullPath)}", fullPath);
}
catch (Exception ex)
{
return new FileWriteResultDto(false, ex.Message, null);
}
}
public FileWriteResultDto CreateDirectory(string path)
{
try
{
var fullPath = Path.GetFullPath(path);
Directory.CreateDirectory(fullPath);
return new FileWriteResultDto(true, $"Created {Path.GetFileName(fullPath)}", fullPath);
}
catch (Exception ex)
{
return new FileWriteResultDto(false, ex.Message, null);
}
}
public FileWriteResultDto Rename(string oldPath, string newPath, string? rootPath)
{
try
{
var oldFullPath = Path.GetFullPath(oldPath);
var newFullPath = Path.GetFullPath(newPath);
if (!IsWithinRoot(rootPath, oldFullPath) || !IsWithinRoot(rootPath, newFullPath))
{
return new FileWriteResultDto(false, "Operation blocked because the target is outside the current project root.", null);
}
if (Directory.Exists(oldFullPath))
{
Directory.Move(oldFullPath, newFullPath);
}
else
{
File.Move(oldFullPath, newFullPath);
}
return new FileWriteResultDto(true, $"Renamed to {Path.GetFileName(newFullPath)}", newFullPath);
}
catch (Exception ex)
{
return new FileWriteResultDto(false, ex.Message, null);
}
}
public FileWriteResultDto Delete(string path, string? rootPath)
{
try
{
var fullPath = Path.GetFullPath(path);
if (!IsWithinRoot(rootPath, fullPath))
{
return new FileWriteResultDto(false, "Operation blocked because the target is outside the current project root.", null);
}
if (!string.IsNullOrWhiteSpace(rootPath) && Path.GetFullPath(rootPath) == fullPath)
{
return new FileWriteResultDto(false, "Deleting the project root is not allowed.", null);
}
var target = TrashTarget(fullPath);
if (Directory.Exists(fullPath))
{
Directory.Move(fullPath, target);
}
else
{
File.Move(fullPath, target);
}
return new FileWriteResultDto(true, $"Moved {Path.GetFileName(fullPath)} to LyricFlow trash", target);
}
catch (Exception ex)
{
return new FileWriteResultDto(false, ex.Message, null);
}
}
#endregion
// MARK: - Workspace Safety
#region Workspace Safety
public bool IsWithinRoot(string? rootPath, string candidatePath)
{
if (string.IsNullOrWhiteSpace(rootPath))
{
return true;
}
try
{
var rootFullPath = Path.GetFullPath(rootPath);
var candidateFullPath = Path.GetFullPath(candidatePath);
var relativePath = Path.GetRelativePath(rootFullPath, candidateFullPath);
return !relativePath.StartsWith("..", StringComparison.Ordinal) && !Path.IsPathRooted(relativePath);
}
catch
{
return false;
}
}
#endregion
// MARK: - Trash Helpers
#region Trash Helpers
private static string TrashTarget(string path)
{
var trashDir = AppPaths.ExplorerTrashDirectory();
var timestamp = DateTime.Now.ToString("yyyyMMdd-HHmmss");
var name = Path.GetFileName(path);
var target = Path.Combine(trashDir, $"{timestamp}-{name}");
var counter = 1;
while (File.Exists(target) || Directory.Exists(target))
{
target = Path.Combine(trashDir, $"{timestamp}-{counter}-{name}");
counter++;
}
return target;
}
#endregion
}