using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Pdftract.Models; namespace Pdftract; /// /// Synchronous (blocking) wrappers for async Pdftract methods. /// These methods are discouraged for production use in async contexts /// as they can lead to thread-pool starvation. /// public sealed partial class Pdftract { /// /// Extracts structured data from a PDF (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public Document Extract(Source source, ExtractOptions? options = null) { return ExtractAsync(source, options, CancellationToken.None).GetAwaiter().GetResult(); } /// /// Extracts plain text from a PDF (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public string ExtractText(Source source, ExtractOptions? options = null) { return ExtractTextAsync(source, options, CancellationToken.None).GetAwaiter().GetResult(); } /// /// Extracts markdown-formatted text from a PDF (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public string ExtractMarkdown(Source source, ExtractOptions? options = null) { return ExtractMarkdownAsync(source, options, CancellationToken.None).GetAwaiter().GetResult(); } /// /// Extracts pages from a PDF as a stream (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public IEnumerable ExtractStream(Source source, ExtractOptions? options = null) { return ExtractStreamAsync(source, options, CancellationToken.None) .ToBlockingEnumerable(); } /// /// Searches for a pattern in a PDF (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public IEnumerable Search(Source source, string pattern, SearchOptions? options = null) { return SearchAsync(source, pattern, options, CancellationToken.None) .ToBlockingEnumerable(); } /// /// Extracts metadata from a PDF (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public Metadata GetMetadata(Source source, ExtractOptions? options = null) { return GetMetadataAsync(source, options, CancellationToken.None).GetAwaiter().GetResult(); } /// /// Computes the fingerprint hash of a PDF (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public Fingerprint Hash(Source source, HashOptions? options = null) { return HashAsync(source, options, CancellationToken.None).GetAwaiter().GetResult(); } /// /// Classifies a PDF document (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public Classification Classify(Source source) { return ClassifyAsync(source, CancellationToken.None).GetAwaiter().GetResult(); } /// /// Verifies a cryptographic receipt for a PDF (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public bool VerifyReceipt(string path, Receipt receipt) { return VerifyReceiptAsync(path, receipt, CancellationToken.None).GetAwaiter().GetResult(); } /// /// Returns the pdftract binary version (synchronous). /// /// /// This synchronous wrapper is provided for legacy code paths. /// In async contexts, prefer instead. /// [SuppressMessage("Usage", "CA1849:Call async methods when in an async context", Justification = "Intentional sync wrapper")] public string GetVersion() { return GetVersionAsync(CancellationToken.None).GetAwaiter().GetResult(); } } file static class AsyncEnumerableExtensions { public static IEnumerable ToBlockingEnumerable(this IAsyncEnumerable asyncEnumerable) { if (asyncEnumerable is null) { throw new ArgumentNullException(nameof(asyncEnumerable)); } return new BlockingAsyncEnumerable(asyncEnumerable); } private sealed class BlockingAsyncEnumerable(IAsyncEnumerable source) : IEnumerable { public IEnumerator GetEnumerator() { return new BlockingAsyncEnumerator(source.GetAsyncEnumerator(CancellationToken.None)); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } private sealed class BlockingAsyncEnumerator(IAsyncEnumerator source) : IEnumerator { private T? _current; private bool _disposed; public T Current => _current!; object System.Collections.IEnumerator.Current => Current!; public bool MoveNext() { if (_disposed) { return false; } using var _ = new ManualResetEvent(false); bool moveNextSucceeded = false; Exception? exception = null; Task.Run(async () => { try { moveNextSucceeded = await source.MoveNextAsync(); } catch (Exception ex) { exception = ex; } finally { _.Set(); } }).Wait(); if (exception is not null) { throw exception; } if (moveNextSucceeded) { _current = source.Current; } return moveNextSucceeded; } public void Reset() { throw new NotSupportedException("Reset is not supported on async enumerators"); } public void Dispose() { if (!_disposed) { source.DisposeAsync().AsTask().Wait(); _disposed = true; } } } }