use crate::error::Result;
use crate::sample::Sample;
use std::future::Future;
use std::pin::Pin;
pub trait Source: Send + Sync {
fn open(&self) -> Result<Box<dyn SourceIterator>>;
fn len_hint(&self) -> Option<u64> {
None
}
fn name(&self) -> &str;
}
impl<T> Source for Box<T>
where
T: Source + ?Sized,
{
fn open(&self) -> Result<Box<dyn SourceIterator>> {
(**self).open()
}
fn len_hint(&self) -> Option<u64> {
(**self).len_hint()
}
fn name(&self) -> &str {
(**self).name()
}
}
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait AsyncSource: Send + Sync {
fn open_async(&self) -> BoxFuture<'_, Result<Box<dyn AsyncSourceIterator>>>;
fn len_hint(&self) -> Option<u64> {
None
}
fn name(&self) -> &str;
}
pub trait SourceIterator: Send {
fn next_sample(&mut self) -> Option<Result<Sample>>;
}
impl<I> SourceIterator for I
where
I: Iterator<Item = Result<Sample>> + Send,
{
fn next_sample(&mut self) -> Option<Result<Sample>> {
self.next()
}
}
pub trait AsyncSourceIterator: Send {
fn next_sample_async(&mut self) -> BoxFuture<'_, Option<Result<Sample>>>;
}
struct SyncSourceAdapter {
inner: Box<dyn SourceIterator>,
}
impl AsyncSourceIterator for SyncSourceAdapter {
fn next_sample_async(&mut self) -> BoxFuture<'_, Option<Result<Sample>>> {
Box::pin(async move { self.inner.next_sample() })
}
}
impl<T> AsyncSource for T
where
T: Source,
{
fn open_async(&self) -> BoxFuture<'_, Result<Box<dyn AsyncSourceIterator>>> {
Box::pin(async move {
self.open()
.map(|inner| Box::new(SyncSourceAdapter { inner }) as Box<dyn AsyncSourceIterator>)
})
}
fn len_hint(&self) -> Option<u64> {
Source::len_hint(self)
}
fn name(&self) -> &str {
Source::name(self)
}
}
pub struct AsyncToSyncAdapter<A> {
inner: A,
}
impl<A: AsyncSource> AsyncToSyncAdapter<A> {
pub fn new(inner: A) -> Self {
Self { inner }
}
}
impl<A: AsyncSource + 'static> Source for AsyncToSyncAdapter<A> {
fn open(&self) -> Result<Box<dyn SourceIterator>> {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.enable_all()
.build()
.map_err(|e| crate::error::Error::InvalidConfig {
reason: format!("failed to build tokio runtime for async source: {e}"),
})?;
let mut inner_iter = rt.block_on(self.inner.open_async())?;
let (tx, rx) = crossbeam_channel::bounded(128);
rt.spawn(async move {
loop {
match inner_iter.next_sample_async().await {
Some(Ok(sample)) => {
if tx.send(Some(Ok(sample))).is_err() {
break;
}
}
Some(Err(e)) => {
let _ = tx.send(Some(Err(e)));
break;
}
None => {
let _ = tx.send(None);
break;
}
}
}
});
Ok(Box::new(AsyncIteratorAdapter { rt, rx }))
}
fn len_hint(&self) -> Option<u64> {
self.inner.len_hint()
}
fn name(&self) -> &str {
self.inner.name()
}
}
pub struct AsyncIteratorAdapter {
#[allow(dead_code)]
rt: tokio::runtime::Runtime,
rx: crossbeam_channel::Receiver<Option<Result<Sample>>>,
}
impl SourceIterator for AsyncIteratorAdapter {
fn next_sample(&mut self) -> Option<Result<Sample>> {
match self.rx.recv() {
Ok(item) => item,
Err(_) => Some(Err(crate::error::Error::SourceFailed {
source_name: "async_adapter".into(),
reason: "async source channel disconnected unexpectedly".into(),
})),
}
}
}