| 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 + "\\"; |
| } |
| } |
|
|