git-next/crates/config/src/server.rs

250 lines
5.1 KiB
Rust
Raw Normal View History

//
use std::{
collections::BTreeMap,
net::SocketAddr,
path::{Path, PathBuf},
str::FromStr,
};
use kxio::fs::FileSystem;
use secrecy::Secret;
use tracing::info;
2024-06-19 07:02:18 +01:00
use crate::{newtype, ForgeAlias, ForgeConfig, RepoAlias};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("fs: {0}")]
KxioFs(#[from] kxio::fs::Error),
#[error("deserialise toml: {0}")]
TomlDe(#[from] toml::de::Error),
#[error("parse IP addres/port: {0}")]
AddressParse(#[from] std::net::AddrParseError),
}
type Result<T> = core::result::Result<T, Error>;
/// Mapped from the `git-next-server.toml` file
#[derive(
Clone,
Debug,
derive_more::From,
PartialEq,
Eq,
PartialOrd,
Ord,
derive_more::AsRef,
serde::Deserialize,
derive_more::Constructor,
)]
pub struct ServerConfig {
http: Http,
webhook: InboundWebhook,
notification: Notification,
storage: ServerStorage,
pub forge: BTreeMap<String, ForgeConfig>,
}
impl ServerConfig {
#[tracing::instrument(skip_all)]
pub fn load(fs: &FileSystem) -> Result<Self> {
let file = fs.base().join("git-next-server.toml");
info!(?file, "");
let str = fs.file_read_to_string(&file)?;
Ok(toml::from_str(&str)?)
}
pub fn forges(&self) -> impl Iterator<Item = (ForgeAlias, &ForgeConfig)> {
self.forge
.iter()
.map(|(alias, forge)| (ForgeAlias::new(alias.clone()), forge))
}
pub const fn storage(&self) -> &ServerStorage {
&self.storage
}
pub const fn notification(&self) -> &Notification {
&self.notification
}
pub const fn inbound_webhook(&self) -> &InboundWebhook {
&self.webhook
}
pub fn http(&self) -> Result<SocketAddr> {
self.http.socket_addr()
}
}
/// Defines the port the server will listen to for incoming webhooks messages
#[derive(
Clone,
Debug,
derive_more::From,
PartialEq,
Eq,
PartialOrd,
Ord,
derive_more::AsRef,
serde::Deserialize,
derive_more::Constructor,
)]
pub struct Http {
addr: String,
port: u16,
}
impl Http {
fn socket_addr(&self) -> Result<SocketAddr> {
Ok(SocketAddr::from_str(&format!(
"{}:{}",
self.addr, self.port
))?)
}
}
/// Defines the Webhook Forges should send updates to
/// Must be an address that is accessible from the remote forge
#[derive(
Clone,
Debug,
derive_more::From,
PartialEq,
Eq,
PartialOrd,
Ord,
derive_more::AsRef,
serde::Deserialize,
derive_more::Constructor,
)]
pub struct InboundWebhook {
url: String,
}
impl InboundWebhook {
pub fn url(&self, forge_alias: &ForgeAlias, repo_alias: &RepoAlias) -> WebhookUrl {
let base_url = &self.url;
WebhookUrl(format!("{base_url}/{forge_alias}/{repo_alias}"))
}
pub fn base_url(&self) -> &str {
&self.url
}
}
newtype!(WebhookUrl: String, serde::Serialize: "The URL for the webhook where forges should send their updates");
/// The directory to store server data, such as cloned repos
#[derive(
Clone,
Debug,
derive_more::From,
PartialEq,
Eq,
PartialOrd,
Ord,
derive_more::AsRef,
serde::Deserialize,
derive_more::Constructor,
)]
pub struct ServerStorage {
path: PathBuf,
}
impl ServerStorage {
pub fn path(&self) -> &Path {
self.path.as_path()
}
}
/// Identifier for the type of Notification
#[derive(
Clone,
Default,
Debug,
derive_more::From,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Deserialize,
Copy,
)]
pub enum NotificationType {
#[default]
None,
Webhook,
}
/// Defines the Webhook Forges should send updates to
/// Must be an address that is accessible from the remote forge
#[derive(
Clone,
Debug,
derive_more::From,
PartialEq,
Eq,
PartialOrd,
Ord,
derive_more::AsRef,
serde::Deserialize,
)]
pub struct Notification {
r#type: NotificationType,
webhook: Option<OutboundWebhook>,
}
impl Notification {
pub const fn none() -> Self {
Self {
r#type: NotificationType::None,
webhook: None,
}
}
pub const fn webhook(webhook: OutboundWebhook) -> Self {
Self {
r#type: NotificationType::Webhook,
webhook: Some(webhook),
}
}
pub const fn r#type(&self) -> NotificationType {
self.r#type
}
pub fn webhook_url(&self) -> Option<String> {
self.webhook.clone().map(|x| x.url)
}
pub fn webhook_secret(&self) -> Option<Secret<String>> {
self.webhook.clone().map(|x| x.secret).map(Secret::new)
}
}
impl Default for Notification {
fn default() -> Self {
Self::none()
}
}
#[derive(
Clone,
Debug,
derive_more::From,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Deserialize,
derive_more::Constructor,
)]
pub struct OutboundWebhook {
url: String,
secret: String,
}
impl OutboundWebhook {
pub fn url(&self) -> &str {
self.url.as_ref()
}
pub fn secret(&self) -> Secret<String> {
Secret::new(self.secret.clone())
}
}