yog-script-analyze 0.4.0

Yog Script (.ys, .yns) analyzed: its lexer, parser and syntax tree, names resolved, the class index model — what the build and the editors read a source with.
Documentation
namespace System.Logging;

using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;

/// One entry a queued provider writes later: its message made when it was
/// logged (its state may change after), what else it was logged with.
public readonly record QueuedEntry(LogLevel Level, string Category, EventId EventId, string Message, Error? Error);

/// A provider writing its entries later, in order, off the thread that
/// logged them (as .NET's console provider queues its own): logging only
/// puts an entry in a channel; a task of its own awaits `WriteAsync` of each.
/// `FlushAsync` waits for what's queued so far; disposing it writes the
/// rest, then stops.
public abstract readonly class QueuedLoggerProvider : ILoggerProvider, IAsyncDisposable {
    private readonly Channel<Queued> _queue = Channel.CreateUnbounded<Queued>();
    private readonly Once<Task> _writing = new Once<Task>();

    /// What it's asked to write: an entry, or where a flush waits.
    private readonly record Queued(QueuedEntry? Entry, TaskCompletionSource? Flushed);

    /// Writes `entry` (to a file, a socket, the game's log).
    protected abstract Task WriteAsync(QueuedEntry entry);

    /// Whether entries of `level` are queued at all (all but `None`).
    protected virtual bool Enabled(LogLevel level) => level != LogLevel.None;

    public readonly ILogger CreateLogger(string categoryName) => new QueuedLogger(this, categoryName);

    /// `entry` queued, its writer started the first time.
    private void Enqueue(QueuedEntry entry) {
        Started();
        _queue.Writer.TryWrite(new Queued(entry, null));
    }

    /// Waits for every entry queued so far to be written.
    public Task FlushAsync() {
        Started();
        let flushed = new TaskCompletionSource();
        _queue.Writer.TryWrite(new Queued(null, flushed));
        return flushed.Task;
    }

    /// Its writer, started once.
    private Task Started() => _writing.GetOrInit(() => Task.Run(() => Drain()));

    private async Task Drain() {
        await foreach (let q in _queue.Reader.ReadAllAsync()) {
            if (q.Entry != null) {
                await WriteAsync(q.Entry);
            }
            q.Flushed?.TrySetResult();
        }
    }

    /// Writes what's queued, then stops.
    public async Task DisposeAsync() {
        _queue.Writer.Complete();
        await Started();
    }

    public void Dispose() {
        DisposeAsync().Wait();
    }

    /// A queued provider's logger of a category.
    private readonly sealed class QueuedLogger : ILogger {
        private readonly QueuedLoggerProvider _provider;
        private readonly string _category;

        public QueuedLogger(QueuedLoggerProvider provider, string category) {
            _provider = provider;
            _category = category;
        }

        public readonly void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Error? error, Func<TState, Error?, string> formatter) {
            if (IsEnabled(logLevel)) {
                let message = formatter(state, error);
                _provider.Enqueue(new QueuedEntry(logLevel, _category, eventId, message, error));
            }
        }

        public readonly bool IsEnabled(LogLevel logLevel) => _provider.Enabled(logLevel);

        public readonly IDisposable? BeginScope<TState>(TState state) => NullScope.Instance;
    }
}