Public Access
181 lines
5.4 KiB
C#
181 lines
5.4 KiB
C#
namespace MeetingAssistant.Workflow;
|
|
|
|
internal sealed class WorkflowRulesEditorMarkdownLinkResolver
|
|
{
|
|
private static readonly string[] ImageUriSchemes = ["http", "https", "file"];
|
|
private readonly IReadOnlyList<string> rootPaths;
|
|
private readonly IReadOnlyList<string> recursiveRootPaths;
|
|
private readonly Dictionary<string, string> resolvedFiles = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public WorkflowRulesEditorMarkdownLinkResolver(
|
|
IEnumerable<string> rootPaths,
|
|
IEnumerable<string>? recursiveRootPaths = null)
|
|
{
|
|
this.rootPaths = rootPaths
|
|
.Where(path => !string.IsNullOrWhiteSpace(path))
|
|
.Select(path => Path.GetFullPath(path))
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.ToArray();
|
|
this.recursiveRootPaths = (recursiveRootPaths ?? [])
|
|
.Where(path => !string.IsNullOrWhiteSpace(path))
|
|
.Select(path => Path.GetFullPath(path))
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.ToArray();
|
|
}
|
|
|
|
public static WorkflowRulesEditorMarkdownLinkResolver Empty { get; } = new([]);
|
|
|
|
public static WorkflowRulesEditorMarkdownLinkResolver FromOptions(MeetingAssistantOptions options)
|
|
{
|
|
var artifactRoots = new[]
|
|
{
|
|
VaultPath.Resolve(options.Vault, options.Vault.AssistantContextFolder),
|
|
VaultPath.Resolve(options.Vault, options.Vault.MeetingNotesFolder),
|
|
VaultPath.Resolve(options.Vault, options.Vault.SummariesFolder),
|
|
VaultPath.Resolve(options.Vault, options.Vault.TranscriptsFolder),
|
|
VaultPath.Resolve(options.Vault, options.Vault.ProjectsFolder)
|
|
};
|
|
|
|
return new WorkflowRulesEditorMarkdownLinkResolver([
|
|
..artifactRoots,
|
|
Environment.CurrentDirectory,
|
|
AppContext.BaseDirectory
|
|
], artifactRoots);
|
|
}
|
|
|
|
public bool TryResolveImageUri(string? target, out Uri uri)
|
|
{
|
|
uri = null!;
|
|
if (string.IsNullOrWhiteSpace(target))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (Uri.TryCreate(target, UriKind.Absolute, out uri!) &&
|
|
ImageUriSchemes.Contains(uri.Scheme, StringComparer.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (TryResolveExistingFilePath(target, out var path))
|
|
{
|
|
uri = new Uri(path);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public string ResolveOpenTarget(string? target)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(target))
|
|
{
|
|
return "";
|
|
}
|
|
|
|
if (Uri.TryCreate(target, UriKind.Absolute, out var uri))
|
|
{
|
|
return uri.Scheme.Equals("file", StringComparison.OrdinalIgnoreCase)
|
|
? uri.LocalPath
|
|
: uri.ToString();
|
|
}
|
|
|
|
return TryResolveExistingFilePath(target, out var path)
|
|
? path
|
|
: target;
|
|
}
|
|
|
|
private bool TryResolveExistingFilePath(string target, out string path)
|
|
{
|
|
path = "";
|
|
if (Path.IsPathRooted(target) && File.Exists(target))
|
|
{
|
|
path = Path.GetFullPath(target);
|
|
return true;
|
|
}
|
|
|
|
if (resolvedFiles.TryGetValue(target, out var cachedPath))
|
|
{
|
|
path = cachedPath;
|
|
return true;
|
|
}
|
|
|
|
if (TryResolveDirectRelativePath(target, out path) ||
|
|
TryFindRelativePathInRoots(target, out path))
|
|
{
|
|
resolvedFiles[target] = path;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool TryResolveDirectRelativePath(string target, out string path)
|
|
{
|
|
foreach (var root in rootPaths)
|
|
{
|
|
try
|
|
{
|
|
var candidate = Path.GetFullPath(target, root);
|
|
if (File.Exists(candidate))
|
|
{
|
|
path = candidate;
|
|
return true;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
path = "";
|
|
return false;
|
|
}
|
|
|
|
private bool TryFindRelativePathInRoots(string target, out string path)
|
|
{
|
|
path = "";
|
|
var normalizedSuffix = NormalizeRelativePath(target);
|
|
if (string.IsNullOrWhiteSpace(normalizedSuffix))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var fileName = Path.GetFileName(normalizedSuffix);
|
|
if (string.IsNullOrWhiteSpace(fileName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach (var root in recursiveRootPaths.Where(Directory.Exists))
|
|
{
|
|
try
|
|
{
|
|
var match = Directory
|
|
.EnumerateFiles(root, fileName, SearchOption.AllDirectories)
|
|
.Where(candidate => NormalizeRelativePath(Path.GetRelativePath(root, candidate))
|
|
.EndsWith(normalizedSuffix, StringComparison.OrdinalIgnoreCase))
|
|
.OrderByDescending(File.GetLastWriteTimeUtc)
|
|
.FirstOrDefault();
|
|
if (match is not null)
|
|
{
|
|
path = match;
|
|
return true;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static string NormalizeRelativePath(string path)
|
|
{
|
|
return path
|
|
.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)
|
|
.TrimStart(Path.DirectorySeparatorChar);
|
|
}
|
|
}
|