use async_trait::async_trait;
use std::{fmt::Debug, time::Duration};

#[cfg(feature = "database")]
pub mod database;
#[cfg(feature = "dynamodb")]
pub mod dynamodb;
#[cfg(feature = "memory")]
pub mod memory;
pub mod null;
#[cfg(feature = "redis")]
pub mod redis;

#[cfg(feature = "database")]
pub use database::DatabaseDriver;
#[cfg(feature = "dynamodb")]
pub use dynamodb::DynamoDBDriver;
#[cfg(feature = "memory")]
pub use memory::MemoryDriver;
pub use null::NullDriver;
#[cfg(feature = "redis")]
pub use redis::RedisDriver;

/// Cache driver.
#[async_trait]
pub trait Driver: Debug + Send + Sync {
	/// Get a value from the cache.
	async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, Error>;

	/// Check if a value exists in the cache.
	async fn has(&self, key: &str) -> Result<bool, Error>;

	/// Put a value into the cache.
	async fn put(&self, key: &str, data: Vec<u8>, expiry: Option<Duration>) -> Result<(), Error>;

	/// Remove a value from the cache.
	async fn forget(&self, key: &str) -> Result<(), Error>;

	/// Remove all values from the cache.
	async fn flush(&self) -> Result<(), Error>;
}

#[derive(Debug, thiserror::Error)]
pub enum Error {
	#[error(transparent)]
	Other(Box<dyn std::error::Error + Send + Sync>),
	#[error("failed to serialize cache value: {0}")]
	Serialization(Box<dyn std::error::Error + Send + Sync>),
}