use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{BoardData, Vestaboard};
const SUBSCRIPTION_API_KEY_HEADER: &str = "X-Vestaboard-Api-Key";
const SUBSCRIPTION_API_SECRET_HEADER: &str = "X-Vestaboard-Api-Secret";
const LIST_SUBSCRIPTIONS_URI: &str = "https://subscriptions.vestaboard.com/subscriptions";
#[derive(Debug, Clone)]
pub struct SubscriptionConfig {
pub api_key: String,
pub api_secret: String,
}
impl<const ROWS: usize, const COLS: usize> Vestaboard<SubscriptionConfig, ROWS, COLS> {
pub fn new_subscription_api(config: SubscriptionConfig) -> Self {
use std::str::FromStr;
let headers = reqwest::header::HeaderMap::from_iter([
(
reqwest::header::CONTENT_TYPE,
reqwest::header::HeaderValue::from_static("application/json"),
),
(
reqwest::header::HeaderName::from_str(SUBSCRIPTION_API_KEY_HEADER).unwrap(),
reqwest::header::HeaderValue::from_str(&config.api_key).expect("failed to parse api key"),
),
(
reqwest::header::HeaderName::from_str(SUBSCRIPTION_API_SECRET_HEADER).unwrap(),
reqwest::header::HeaderValue::from_str(&config.api_secret).expect("failed to parse api secret"),
),
]);
Vestaboard {
client: reqwest::Client::builder()
.default_headers(headers)
.user_agent(format!("vestaboard-rs/{}", env!("CARGO_PKG_VERSION")))
.build()
.expect("failed to build reqwest client"),
config,
}
}
pub async fn get_subscriptions(&self) -> Result<SubscriptionsList, SubscriptionApiError> {
let res = self.client.get(LIST_SUBSCRIPTIONS_URI).send().await?;
if !res.status().is_success() {
return Err(SubscriptionApiError::ApiError(res.text().await?));
}
Ok(res.json::<SubscriptionsList>().await?)
}
pub async fn write(
&self,
subscription_id: &str,
message: BoardData<ROWS, COLS>,
) -> Result<SubscriptionMessageResponse, SubscriptionApiError> {
let message = SubscriptionMessage { characters: message };
let res = self
.client
.post(&format!(
"https://subscriptions.vestaboard.com/subscriptions/{}/message",
subscription_id
))
.json(&message)
.send()
.await?;
if !res.status().is_success() {
return Err(SubscriptionApiError::ApiError(res.text().await?));
}
Ok(res.json::<SubscriptionMessageResponse>().await?)
}
}
#[derive(Debug, Clone, Serialize)]
struct SubscriptionMessage<const ROWS: usize, const COLS: usize> {
characters: BoardData<ROWS, COLS>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct SubscriptionMessageResponse {
pub id: String,
pub created: String,
pub muted: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Subscription {
pub id: String,
pub board_id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct SubscriptionsList(pub Vec<Subscription>);
#[derive(Error, Debug)]
pub enum SubscriptionApiError {
#[error("reqwest error: {0}")]
Reqwest(#[from] reqwest::Error),
#[error("failed to parse response: {0}")]
Deserialize(#[from] serde_json::Error),
#[error("api error: {0}")]
ApiError(String),
}