#![warn(missing_docs)]
use async_trait::async_trait;
use thiserror::Error;
use nostr_bbs_config::ForumConfig;
#[derive(Debug, Error)]
pub enum SetupError {
#[error("provider: {0}")]
Provider(String),
#[error("config: {0}")]
Config(String),
#[error("io: {0}")]
Io(String),
#[error("unsupported")]
Unsupported,
#[error("{provider}::{method} not yet implemented — scheduled for Sprint v12+")]
NotYetImplemented {
provider: &'static str,
method: &'static str,
},
}
#[derive(Debug, Clone)]
pub struct ProvisionedResource {
pub kind: String,
pub id: String,
pub name: String,
}
#[async_trait(?Send)]
pub trait Provider {
fn tier(&self) -> &'static str;
async fn provision(&self, cfg: &ForumConfig) -> Result<Vec<ProvisionedResource>, SetupError>;
async fn render_wrangler(
&self,
cfg: &ForumConfig,
resources: &[ProvisionedResource],
) -> Result<String, SetupError>;
}
pub struct SelfHostProvider;
#[async_trait(?Send)]
impl Provider for SelfHostProvider {
fn tier(&self) -> &'static str {
"tier-1"
}
async fn provision(&self, _cfg: &ForumConfig) -> Result<Vec<ProvisionedResource>, SetupError> {
Ok(Vec::new())
}
async fn render_wrangler(
&self,
_cfg: &ForumConfig,
_resources: &[ProvisionedResource],
) -> Result<String, SetupError> {
Err(SetupError::NotYetImplemented {
provider: "SelfHostProvider",
method: "render_wrangler",
})
}
}
pub struct CloudflareWorkersProvider;
#[async_trait(?Send)]
impl Provider for CloudflareWorkersProvider {
fn tier(&self) -> &'static str {
"tier-2"
}
async fn provision(&self, _cfg: &ForumConfig) -> Result<Vec<ProvisionedResource>, SetupError> {
Err(SetupError::NotYetImplemented {
provider: "CloudflareWorkersProvider",
method: "provision",
})
}
async fn render_wrangler(
&self,
_cfg: &ForumConfig,
_resources: &[ProvisionedResource],
) -> Result<String, SetupError> {
Err(SetupError::NotYetImplemented {
provider: "CloudflareWorkersProvider",
method: "render_wrangler",
})
}
}
pub struct FlyDotIoProvider;
#[async_trait(?Send)]
impl Provider for FlyDotIoProvider {
fn tier(&self) -> &'static str {
"tier-3"
}
async fn provision(&self, _cfg: &ForumConfig) -> Result<Vec<ProvisionedResource>, SetupError> {
Err(SetupError::NotYetImplemented {
provider: "FlyDotIoProvider",
method: "provision",
})
}
async fn render_wrangler(
&self,
_cfg: &ForumConfig,
_resources: &[ProvisionedResource],
) -> Result<String, SetupError> {
Err(SetupError::NotYetImplemented {
provider: "FlyDotIoProvider",
method: "render_wrangler",
})
}
}
pub struct TurnkeyProvider;
#[async_trait(?Send)]
impl Provider for TurnkeyProvider {
fn tier(&self) -> &'static str {
"tier-4"
}
async fn provision(&self, _cfg: &ForumConfig) -> Result<Vec<ProvisionedResource>, SetupError> {
Err(SetupError::NotYetImplemented {
provider: "TurnkeyProvider",
method: "provision",
})
}
async fn render_wrangler(
&self,
_cfg: &ForumConfig,
_resources: &[ProvisionedResource],
) -> Result<String, SetupError> {
Err(SetupError::Unsupported)
}
}
pub struct KubernetesProvider;
#[async_trait(?Send)]
impl Provider for KubernetesProvider {
fn tier(&self) -> &'static str {
"tier-x"
}
async fn provision(&self, _cfg: &ForumConfig) -> Result<Vec<ProvisionedResource>, SetupError> {
Err(SetupError::NotYetImplemented {
provider: "KubernetesProvider",
method: "provision",
})
}
async fn render_wrangler(
&self,
_cfg: &ForumConfig,
_resources: &[ProvisionedResource],
) -> Result<String, SetupError> {
Err(SetupError::NotYetImplemented {
provider: "KubernetesProvider",
method: "render_wrangler",
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn provider_tiers_match_taxonomy() {
assert_eq!(SelfHostProvider.tier(), "tier-1");
assert_eq!(CloudflareWorkersProvider.tier(), "tier-2");
assert_eq!(FlyDotIoProvider.tier(), "tier-3");
assert_eq!(TurnkeyProvider.tier(), "tier-4");
assert_eq!(KubernetesProvider.tier(), "tier-x");
}
}