📄 cli/src/ScanCommand.cs
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Cfa.Cli;

internal sealed class ScanCommand : Command
{
    private static Argument<FileInfo> PathArgument { get; } =
        new("path")
        {
            Description =
                "Path to the solution file, or the directory containing it. (Defaults to the current directory.)",
            DefaultValueFactory = result =>
            {
                var directory = new DirectoryInfo(Directory.GetCurrentDirectory());
                switch (
                    directory
                        .GetFiles("*.sln?", SearchOption.TopDirectoryOnly)
                        .Where(f => f.Extension is ".sln" or ".slnx")
                        .ToArray()
                )
                {
                    case []:
                        result.AddError("No solution files found in the current directory.");
                        return null!;
                    case [var file]:
                        return file;
                    default:
                        result.AddError("Multiple solution files found in the current directory.");
                        return null!;
                }
            },
            CustomParser = result =>
            {
                if (result.Tokens is not [var token] || string.IsNullOrWhiteSpace(token.Value))
                {
                    result.AddError("No path specified.");
                    return null;
                }
                if (Directory.Exists(token.Value))
                {
                    var directory = new DirectoryInfo(token.Value);
                    switch (
                        directory
                            .GetFiles("*.sln?", SearchOption.TopDirectoryOnly)
                            .Where(f => f.Extension is ".sln" or ".slnx")
                            .ToArray()
                    )
                    {
                        case []:
                            result.AddError("No solution files found in the specified directory.");
                            return null;
                        case [var file]:
                            return file;
                        default:
                            result.AddError("Multiple solution files found in the specified directory.");
                            return null;
                    }
                }
                if (File.Exists(token.Value))
                {
                    return new FileInfo(token.Value);
                }
                result.AddError("The specified path does not exist.");
                return null;
            },
        };

    public ScanCommand()
        : base("scan", "Scan the code for analysis")
    {
        Action = new ScanAction();
        Arguments.Add(PathArgument);
    }

    internal sealed class ScanAction : AsynchronousCommandLineAction
    {
        public override async Task<int> InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
        {
            var solutionFile = parseResult.GetRequiredValue(PathArgument);
            using var scanner = await Scanner.Create(solutionFile, cancellationToken);

            await foreach (var method in scanner.FindMethods(cancellationToken))
            {
                Console.WriteLine($"Found method: {method.Name}");
            }

            return 0;
        }
    }
}