use crate::core::types::{Fid, Message, MessageId, MessageType};
use async_trait::async_trait;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataAccessError {
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
#[error("Redis error: {0}")]
Redis(String),
#[error("Message not found: {0}")]
NotFound(String),
#[error("Search engine error: {0}")]
Search(String),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Hub client error: {0}")]
HubClient(String),
#[error("Other error: {0}")]
Other(String),
}
pub type Result<T> = std::result::Result<T, DataAccessError>;
#[async_trait]
pub trait Database: Send + Sync {
async fn get_message(&self, id: &MessageId, message_type: MessageType) -> Result<Message>;
async fn get_messages_by_fid(
&self,
fid: Fid,
message_type: MessageType,
limit: usize,
cursor: Option<MessageId>,
) -> Result<Vec<Message>>;
async fn store_message(&self, message: Message) -> Result<()>;
async fn delete_message(&self, id: &MessageId, message_type: MessageType) -> Result<()>;
}
#[async_trait]
pub trait HubClient: Send + Sync {
async fn get_user_data_by_fid(&self, fid: Fid, limit: usize) -> Result<Vec<Message>>;
async fn get_user_data(&self, fid: Fid, data_type: &str) -> Result<Option<Message>>;
async fn get_username_proofs_by_fid(&self, fid: Fid) -> Result<Vec<Message>>;
async fn get_username_proof_by_name(
&self,
name: &str,
) -> Result<Option<crate::proto::UserNameProof>>;
async fn get_verifications_by_fid(&self, fid: Fid, limit: usize) -> Result<Vec<Message>>;
async fn get_casts_by_fid(&self, fid: Fid, limit: usize) -> Result<Vec<Message>>;
async fn get_cast(&self, fid: Fid, hash: &[u8]) -> Result<Option<Message>>;
async fn get_casts_by_mention(&self, fid: Fid, limit: usize) -> Result<Vec<Message>>;
async fn get_casts_by_parent(
&self,
parent_fid: Fid,
parent_hash: &[u8],
limit: usize,
) -> Result<Vec<Message>>;
async fn get_casts_by_parent_url(&self, parent_url: &str, limit: usize)
-> Result<Vec<Message>>;
async fn get_all_casts_by_fid(
&self,
fid: Fid,
limit: usize,
start_time: Option<u64>,
end_time: Option<u64>,
) -> Result<Vec<Message>>;
async fn get_reaction(
&self,
fid: Fid,
reaction_type: u8,
target_cast_fid: Option<Fid>,
target_cast_hash: Option<&[u8]>,
target_url: Option<&str>,
) -> Result<Option<Message>>;
async fn get_reactions_by_fid(
&self,
fid: Fid,
reaction_type: Option<u8>,
limit: usize,
) -> Result<Vec<Message>>;
async fn get_reactions_by_target(
&self,
target_cast_fid: Option<Fid>,
target_cast_hash: Option<&[u8]>,
target_url: Option<&str>,
reaction_type: Option<u8>,
limit: usize,
) -> Result<Vec<Message>>;
async fn get_all_reactions_by_fid(
&self,
fid: Fid,
limit: usize,
start_time: Option<u64>,
end_time: Option<u64>,
) -> Result<Vec<Message>>;
async fn get_link(&self, fid: Fid, link_type: &str, target_fid: Fid)
-> Result<Option<Message>>;
async fn get_links_by_fid(
&self,
fid: Fid,
link_type: Option<&str>,
limit: usize,
) -> Result<Vec<Message>>;
async fn get_links_by_target(
&self,
target_fid: Fid,
link_type: Option<&str>,
limit: usize,
) -> Result<Vec<Message>>;
async fn get_link_compact_state_by_fid(&self, fid: Fid) -> Result<Vec<Message>>;
async fn get_all_links_by_fid(
&self,
fid: Fid,
limit: usize,
start_time: Option<u64>,
end_time: Option<u64>,
) -> Result<Vec<Message>>;
}
#[derive(Debug, Clone)]
pub struct DataContext<DB, HC> {
database: Option<DB>,
hub_client: Option<HC>,
}
impl<DB, HC> DataContext<DB, HC>
where
DB: Database,
HC: HubClient,
{
pub async fn get_user_data_by_fid(&self, fid: Fid, limit: usize) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
match hub.get_user_data_by_fid(fid, limit).await {
Ok(data) if !data.is_empty() => return Ok(data),
_ => {},
}
}
Err(DataAccessError::Other("No data source available".to_string()))
}
pub async fn get_user_data(&self, fid: Fid, data_type: &str) -> Result<Option<Message>> {
if let Some(hub) = &self.hub_client {
if let Ok(data) = hub.get_user_data(fid, data_type).await {
return Ok(data);
}
}
Err(DataAccessError::Other("No data source available".to_string()))
}
pub async fn get_username_proofs_by_fid(&self, fid: Fid) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_username_proofs_by_fid(fid).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_fid_by_username(&self, username: &str) -> Result<Option<Fid>> {
if let Some(hub) = &self.hub_client {
let proof = hub.get_username_proof_by_name(username).await?;
return Ok(proof.map(|p| Fid::new(p.fid)));
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_verifications_by_fid(&self, fid: Fid, limit: usize) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_verifications_by_fid(fid, limit).await;
}
if let Some(db) = &self.database {
return db.get_messages_by_fid(fid, MessageType::Verification, limit, None).await;
}
Err(DataAccessError::Other("No data source available".to_string()))
}
pub async fn get_casts_by_fid(&self, fid: Fid, limit: usize) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_casts_by_fid(fid, limit).await;
}
if let Some(db) = &self.database {
return db.get_messages_by_fid(fid, MessageType::Cast, limit, None).await;
}
Err(DataAccessError::Other("No data source available".to_string()))
}
pub async fn get_cast(&self, fid: Fid, hash: &[u8]) -> Result<Option<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_cast(fid, hash).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_casts_by_mention(&self, fid: Fid, limit: usize) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_casts_by_mention(fid, limit).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_casts_by_parent(
&self,
parent_fid: Fid,
parent_hash: &[u8],
limit: usize,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_casts_by_parent(parent_fid, parent_hash, limit).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_casts_by_parent_url(
&self,
parent_url: &str,
limit: usize,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_casts_by_parent_url(parent_url, limit).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_all_casts_by_fid(
&self,
fid: Fid,
limit: usize,
start_time: Option<u64>,
end_time: Option<u64>,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_all_casts_by_fid(fid, limit, start_time, end_time).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_reaction(
&self,
fid: Fid,
reaction_type: u8,
target_cast_fid: Option<Fid>,
target_cast_hash: Option<&[u8]>,
target_url: Option<&str>,
) -> Result<Option<Message>> {
if let Some(hub) = &self.hub_client {
return hub
.get_reaction(fid, reaction_type, target_cast_fid, target_cast_hash, target_url)
.await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_reactions_by_fid(
&self,
fid: Fid,
reaction_type: Option<u8>,
limit: usize,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_reactions_by_fid(fid, reaction_type, limit).await;
}
if let Some(db) = &self.database {
return db.get_messages_by_fid(fid, MessageType::Reaction, limit, None).await;
}
Err(DataAccessError::Other("No data source available".to_string()))
}
pub async fn get_reactions_by_target(
&self,
target_cast_fid: Option<Fid>,
target_cast_hash: Option<&[u8]>,
target_url: Option<&str>,
reaction_type: Option<u8>,
limit: usize,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub
.get_reactions_by_target(
target_cast_fid,
target_cast_hash,
target_url,
reaction_type,
limit,
)
.await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_all_reactions_by_fid(
&self,
fid: Fid,
limit: usize,
start_time: Option<u64>,
end_time: Option<u64>,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_all_reactions_by_fid(fid, limit, start_time, end_time).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_link(
&self,
fid: Fid,
link_type: &str,
target_fid: Fid,
) -> Result<Option<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_link(fid, link_type, target_fid).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_links_by_fid(
&self,
fid: Fid,
link_type: Option<&str>,
limit: usize,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_links_by_fid(fid, link_type, limit).await;
}
if let Some(db) = &self.database {
return db.get_messages_by_fid(fid, MessageType::Link, limit, None).await;
}
Err(DataAccessError::Other("No data source available".to_string()))
}
pub async fn get_links_by_target(
&self,
target_fid: Fid,
link_type: Option<&str>,
limit: usize,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_links_by_target(target_fid, link_type, limit).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_link_compact_state_by_fid(&self, fid: Fid) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_link_compact_state_by_fid(fid).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub async fn get_all_links_by_fid(
&self,
fid: Fid,
limit: usize,
start_time: Option<u64>,
end_time: Option<u64>,
) -> Result<Vec<Message>> {
if let Some(hub) = &self.hub_client {
return hub.get_all_links_by_fid(fid, limit, start_time, end_time).await;
}
Err(DataAccessError::Other("Hub client not available".to_string()))
}
pub fn database(&self) -> Result<&DB> {
self.database
.as_ref()
.ok_or_else(|| DataAccessError::Other("Database not configured".to_string()))
}
pub fn hub(&self) -> Result<&HC> {
self.hub_client
.as_ref()
.ok_or_else(|| DataAccessError::Other("Hub client not configured".to_string()))
}
}
pub struct DataContextBuilder<DB, HC> {
database: Option<DB>,
hub_client: Option<HC>,
}
impl<DB, HC> Default for DataContextBuilder<DB, HC> {
fn default() -> Self {
Self { database: None, hub_client: None }
}
}
impl<DB, HC> DataContextBuilder<DB, HC> {
pub fn new() -> Self {
Self::default()
}
pub fn with_database(mut self, database: DB) -> Self {
self.database = Some(database);
self
}
pub fn with_hub_client(mut self, hub_client: HC) -> Self {
self.hub_client = Some(hub_client);
self
}
pub fn build(self) -> DataContext<DB, HC> {
DataContext { database: self.database, hub_client: self.hub_client }
}
}