using Beam.Models; namespace Beam.Downloaders; public record class UnitDownloaderOptions { public HttpClient Client { get; init; } = new(); public FailurePredicateOptions? FailurePredicateOptions { get; init; } public FragmentOptions? FragmentOptions { get; init; } public required AsyncTransformer AsyncTransformer { get; init; } public string? DownloadFolder { get; init; } = null; public int BufferSize { get; init; } = 80 * 1024; // 80kb } public record class FailurePredicateOptions { public required AsyncDownloadFailurePredicate?[]? AsyncDownloadFailurePredicates { get; init; } public bool ProcessInParallel { get; init; } = false; public int? ParallelThreads { get; init; } } public record class FragmentOptions { public required int FragmentSize { get; init; } public bool DownloadInParallel { get; init; } = false; public int? ParallelThreads { get; init; } } // ---------- UnitDownloaderOptions Builder ---------- public sealed class UnitDownloaderOptionsBuilder { private HttpClient _client = new HttpClient(); private FailurePredicateOptions? _failureOptions; private FragmentOptions? _fragmentOptions; private AsyncTransformer? _asyncTransformer; private string? _downloadFolder = null; private int _bufferSize = 80 * 1024; public UnitDownloaderOptionsBuilder WithClient(HttpClient client) { _client = client ?? throw new System.ArgumentNullException(nameof(client)); return this; } public UnitDownloaderOptionsBuilder WithFailurePredicateOptions(FailurePredicateOptions? options) { _failureOptions = options; return this; } public UnitDownloaderOptionsBuilder WithFailurePredicates(System.Action> configure) { if (configure == null) throw new System.ArgumentNullException(nameof(configure)); var b = new FailurePredicateOptionsBuilder(); configure(b); _failureOptions = b.Build(); return this; } public UnitDownloaderOptionsBuilder WithFragmentOptions(FragmentOptions? options) { _fragmentOptions = options; return this; } public UnitDownloaderOptionsBuilder WithFragments(System.Action configure) { if (configure == null) throw new System.ArgumentNullException(nameof(configure)); var b = new FragmentOptionsBuilder(); configure(b); _fragmentOptions = b.Build(); return this; } public UnitDownloaderOptionsBuilder WithAsyncTransformer(AsyncTransformer transformer) { _asyncTransformer = transformer ?? throw new System.ArgumentNullException(nameof(transformer)); return this; } public UnitDownloaderOptionsBuilder WithDownloadFolder(string? downloadFolder) { _downloadFolder = downloadFolder; return this; } public UnitDownloaderOptionsBuilder WithBufferSize(int bytes) { if (bytes <= 0) throw new System.ArgumentOutOfRangeException(nameof(bytes)); _bufferSize = bytes; return this; } public UnitDownloaderOptions Build() { if (_asyncTransformer == null) throw new System.InvalidOperationException("AsyncTransformer must be provided."); return new UnitDownloaderOptions { Client = _client, FailurePredicateOptions = _failureOptions, FragmentOptions = _fragmentOptions, AsyncTransformer = _asyncTransformer, DownloadFolder = _downloadFolder, BufferSize = _bufferSize }; } } // ---------- FailurePredicateOptions Builder ---------- public sealed class FailurePredicateOptionsBuilder { private readonly System.Collections.Generic.List?> _predicates = new System.Collections.Generic.List?>(); private bool _processInParallel = false; private int? _parallelThreads = null; public FailurePredicateOptionsBuilder WithPredicate(AsyncDownloadFailurePredicate? predicate) { _predicates.Add(predicate); return this; } public FailurePredicateOptionsBuilder WithPredicates(System.Collections.Generic.IEnumerable?> predicates) { if (predicates == null) throw new System.ArgumentNullException(nameof(predicates)); _predicates.AddRange(predicates); return this; } public FailurePredicateOptionsBuilder WithPredicates(params AsyncDownloadFailurePredicate?[] predicates) { _predicates.Clear(); if (predicates != null) _predicates.AddRange(predicates); return this; } public FailurePredicateOptionsBuilder WithProcessInParallel(bool value = true) { _processInParallel = value; return this; } public FailurePredicateOptionsBuilder WithParallelThreads(int? threads) { if (threads.HasValue && threads.Value <= 0) throw new System.ArgumentOutOfRangeException(nameof(threads)); _parallelThreads = threads; return this; } public FailurePredicateOptions Build() { var arr = _predicates.Count == 0 ? [] : _predicates.ToArray(); return new FailurePredicateOptions { AsyncDownloadFailurePredicates = arr, ProcessInParallel = _processInParallel, ParallelThreads = _parallelThreads }; } } // ---------- FragmentOptions Builder ---------- public sealed class FragmentOptionsBuilder { private int? _fragmentSize; private bool _downloadInParallel = false; private int? _parallelThreads = null; public FragmentOptionsBuilder WithFragmentSize(int bytes) { if (bytes <= 0) throw new System.ArgumentOutOfRangeException(nameof(bytes)); _fragmentSize = bytes; return this; } public FragmentOptionsBuilder WithDownloadInParallel(bool value = true) { _downloadInParallel = value; return this; } public FragmentOptionsBuilder WithParallelThreads(int? threads) { if (threads.HasValue && threads.Value <= 0) throw new System.ArgumentOutOfRangeException(nameof(threads)); _parallelThreads = threads; return this; } public FragmentOptions Build() { if (!_fragmentSize.HasValue) throw new System.InvalidOperationException("FragmentSize must be provided."); return new FragmentOptions { FragmentSize = _fragmentSize.Value, DownloadInParallel = _downloadInParallel, ParallelThreads = _parallelThreads }; } }