anshdadhich's picture
initial commit
1c4658f
using System.Diagnostics;
using FastSeek.Core;
namespace FastSeek.Cli;
internal static class Program
{
private static void Main()
{
Console.WriteLine("FastSeek - starting...");
using var engine = new FastSeekEngine();
engine.InitializeAsync(msg => Console.WriteLine($"[startup] {msg}")).GetAwaiter().GetResult();
var configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "fastsearch", "config.txt");
Directory.CreateDirectory(Path.GetDirectoryName(configPath)!);
engine.ExcludedDirs.AddRange(LoadExclusions(configPath));
var resultLimit = 200;
Console.WriteLine("FastSeek - File Search");
PrintMetrics(engine);
Console.WriteLine("Commands: case, count, metrics, options, limit <n>, clearcache, exclusions, exclude <path>, unexclude <path>, quit");
while (true)
{
Console.Write("search> ");
var input = Console.ReadLine()?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(input)) continue;
if (input is "quit" or "exit" or "q") break;
if (input == "case") { engine.CaseSensitive = !engine.CaseSensitive; Console.WriteLine(engine.CaseSensitive ? "case sensitivity: ON" : "case sensitivity: OFF"); continue; }
if (input == "count") { Console.WriteLine($"{engine.Count():N0} files in index"); continue; }
if (input == "metrics") { PrintMetrics(engine); continue; }
if (input == "options") { Console.WriteLine($"case={(engine.CaseSensitive ? "on" : "off")}, limit={resultLimit}, exclusions={engine.ExcludedDirs.Count}, cache={engine.CachePath}"); continue; }
if (input == "clearcache")
{
try
{
if (File.Exists(engine.CachePath)) File.Delete(engine.CachePath);
Console.WriteLine($"cache cleared: {engine.CachePath}");
Console.WriteLine("restart FastSeek to force full rescan");
}
catch (Exception ex)
{
Console.WriteLine($"failed to clear cache: {ex.Message}");
}
continue;
}
if (input.StartsWith("limit ", StringComparison.OrdinalIgnoreCase))
{
if (int.TryParse(input[6..].Trim(), out var parsed) && parsed > 0 && parsed <= 5000)
{
resultLimit = parsed;
Console.WriteLine($"result limit set to {resultLimit}");
}
else Console.WriteLine("usage: limit <1..5000>");
continue;
}
if (input.StartsWith("exclude ", StringComparison.OrdinalIgnoreCase)) { var p = NormalizeExclude(input[8..]); if (!engine.ExcludedDirs.Contains(p)) engine.ExcludedDirs.Add(p); SaveExclusions(configPath, engine.ExcludedDirs); Console.WriteLine($"excluded: {p}"); continue; }
if (input.StartsWith("unexclude ", StringComparison.OrdinalIgnoreCase)) { var p = NormalizeExclude(input[10..]); engine.ExcludedDirs.RemoveAll(x => x == p); SaveExclusions(configPath, engine.ExcludedDirs); Console.WriteLine($"removed: {p}"); continue; }
if (input == "exclusions") { Console.WriteLine(engine.ExcludedDirs.Count == 0 ? "no excluded directories" : string.Join(Environment.NewLine, engine.ExcludedDirs.Select(x => $"- {x}"))); continue; }
var sw = Stopwatch.StartNew();
var results = engine.Search(input, resultLimit);
sw.Stop();
if (results.Count == 0) Console.WriteLine($"no results for \"{input}\"");
else
{
for (var i = 0; i < results.Count; i++) Console.WriteLine($"[{i + 1,4}] [{(results[i].IsDir ? "DIR " : "FILE")}] {results[i].FullPath}");
Console.WriteLine($"\nResults: {results.Count:N0} Time: {sw.Elapsed.TotalMilliseconds:F2} ms Limit: {resultLimit:N0}\n");
}
}
}
private static void PrintMetrics(FastSeekEngine engine)
{
var m = engine.LastStartupMetrics;
if (m is null) return;
Console.WriteLine();
Console.WriteLine("Startup Metrics");
Console.WriteLine("---------------");
Console.WriteLine($"Cache : {(m.CacheLoaded ? "HIT" : "MISS")}");
Console.WriteLine($"Total Files : {m.TotalFiles:N0}");
Console.WriteLine($"Drives : {m.DriveDiscoveryMs,10:F2} ms");
Console.WriteLine($"Cache Load : {m.CacheLoadMs,10:F2} ms");
Console.WriteLine($"Scan : {m.ScanMs,10:F2} ms");
Console.WriteLine($"Index : {m.IndexMs,10:F2} ms");
Console.WriteLine($"Cache Save : {m.CacheSaveMs,10:F2} ms");
Console.WriteLine($"Startup Total: {m.TotalStartupMs,10:F2} ms");
Console.WriteLine($"Cache Path : {engine.CachePath}");
Console.WriteLine();
}
private static List<string> LoadExclusions(string path) => File.Exists(path) ? File.ReadAllLines(path).Select(l => l.Trim().ToLowerInvariant()).Where(l => l.Length > 0).ToList() : [];
private static void SaveExclusions(string path, List<string> dirs) => File.WriteAllText(path, string.Join(Environment.NewLine, dirs));
private static string NormalizeExclude(string path)
{
var p = path.Trim().ToLowerInvariant();
return p.EndsWith("\\") || p.EndsWith("/") ? p : p + "\\";
}
}