use super::{DataFrame, DataQuery, Timeframe};
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AsyncDataError {
#[error("Symbol not found: {0}")]
SymbolNotFound(String),
#[error("No data available for timeframe: {0}")]
TimeframeNotAvailable(String),
#[error("No data in requested range")]
NoDataInRange,
#[error("Connection error: {0}")]
Connection(String),
#[error("Timeout")]
Timeout,
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Provider error: {0}")]
Provider(String),
#[error("Configuration error: {0}")]
Config(String),
}
pub trait AsyncDataProvider: Send + Sync {
fn load<'a>(
&'a self,
query: &'a DataQuery,
) -> Pin<Box<dyn Future<Output = Result<DataFrame, AsyncDataError>> + Send + 'a>>;
fn has_data(&self, symbol: &str, timeframe: &Timeframe) -> bool;
fn symbols(&self) -> Vec<String>;
fn timeframes(&self, symbol: &str) -> Vec<Timeframe> {
let _ = symbol;
Vec::new()
}
fn subscribe(
&self,
symbol: &str,
timeframe: &Timeframe,
) -> Result<tokio::sync::mpsc::Receiver<DataFrame>, AsyncDataError> {
let _ = (symbol, timeframe);
Err(AsyncDataError::Provider(
"Live data not supported by this provider".into(),
))
}
fn unsubscribe(&self, symbol: &str, timeframe: &Timeframe) -> Result<(), AsyncDataError> {
let _ = (symbol, timeframe);
Ok(())
}
}
pub type SharedAsyncProvider = Arc<dyn AsyncDataProvider>;
#[derive(Debug, Clone, Default)]
pub struct NullAsyncProvider;
impl AsyncDataProvider for NullAsyncProvider {
fn load<'a>(
&'a self,
query: &'a DataQuery,
) -> Pin<Box<dyn Future<Output = Result<DataFrame, AsyncDataError>> + Send + 'a>> {
Box::pin(async move { Err(AsyncDataError::SymbolNotFound(query.id.clone())) })
}
fn has_data(&self, _symbol: &str, _timeframe: &Timeframe) -> bool {
false
}
fn symbols(&self) -> Vec<String> {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_null_provider() {
let provider = NullAsyncProvider;
let query = DataQuery::new("TEST", Timeframe::d1());
assert!(!provider.has_data("TEST", &Timeframe::d1()));
assert!(provider.symbols().is_empty());
assert!(provider.load(&query).await.is_err());
}
}