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;
}
}
}
}