git-next/src/server/config.rs

352 lines
9 KiB
Rust
Raw Normal View History

use std::{
collections::HashMap,
fmt::{Display, Formatter},
};
use secrecy::ExposeSecret;
2024-04-07 13:47:39 +01:00
use serde::Deserialize;
use terrors::OneOf;
use crate::filesystem::FileSystem;
2024-04-07 16:09:16 +01:00
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct ServerConfig {
forge: HashMap<String, Forge>,
}
impl ServerConfig {
pub(crate) fn load(fs: &FileSystem) -> Result<Self, OneOf<(std::io::Error, toml::de::Error)>> {
let str = fs.read_file("git-next-server.toml").map_err(OneOf::new)?;
toml::from_str(&str).map_err(OneOf::new)
}
pub(crate) fn forges(&self) -> impl Iterator<Item = (ForgeName, &Forge)> {
self.forge
.iter()
.map(|(name, forge)| (ForgeName(name.clone()), forge))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
2024-04-08 09:54:20 +01:00
pub struct RepoConfig {
branches: RepoBranches,
}
impl RepoConfig {
#[allow(dead_code)]
pub(crate) fn load(toml: &str) -> Result<Self, OneOf<(toml::de::Error,)>> {
toml::from_str(toml).map_err(OneOf::new)
}
pub(crate) const fn branches(&self) -> &RepoBranches {
&self.branches
}
2024-04-08 09:54:20 +01:00
}
2024-04-09 10:44:01 +01:00
impl Display for RepoConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.branches)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
2024-04-08 09:54:20 +01:00
pub struct RepoBranches {
main: String,
next: String,
dev: String,
}
impl RepoBranches {
pub fn main(&self) -> BranchName {
BranchName(self.main.clone())
}
pub fn next(&self) -> BranchName {
BranchName(self.next.clone())
}
pub fn dev(&self) -> BranchName {
BranchName(self.dev.clone())
}
}
2024-04-09 10:44:01 +01:00
impl Display for RepoBranches {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
2024-04-08 09:54:20 +01:00
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Forge {
forge_type: ForgeType,
hostname: String,
2024-04-07 13:47:39 +01:00
user: String,
token: String,
2024-04-07 13:47:39 +01:00
// API Token
// Private SSH Key Path
repos: HashMap<String, Repo>,
}
impl Forge {
#[allow(dead_code)]
pub const fn forge_type(&self) -> &ForgeType {
&self.forge_type
}
pub fn hostname(&self) -> Hostname {
Hostname(self.hostname.clone())
}
pub fn user(&self) -> User {
User(self.user.clone())
}
pub fn token(&self) -> ApiToken {
ApiToken(self.token.clone().into())
}
pub fn repos(&self) -> impl Iterator<Item = (RepoName, &Repo)> {
self.repos
.iter()
.map(|(name, repo)| (RepoName(name.clone()), repo))
}
2024-04-07 13:47:39 +01:00
}
impl Display for Forge {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} - {}@{}", self.forge_type, self.user, self.hostname)
}
}
2024-04-07 13:47:39 +01:00
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Repo {
repo: String,
branch: String,
}
impl Repo {
#[allow(dead_code)]
pub fn repo(&self) -> RepoPath {
RepoPath(self.repo.clone())
}
#[allow(dead_code)]
pub fn branch(&self) -> BranchName {
BranchName(self.branch.clone())
}
}
#[cfg(test)]
2024-04-08 09:54:20 +01:00
impl AsRef<Self> for Repo {
fn as_ref(&self) -> &Self {
self
}
}
impl Display for Repo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} - {}", self.repo, self.branch)
}
}
2024-04-08 08:22:23 +01:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ForgeName(pub String);
impl Display for ForgeName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
2024-04-08 08:22:23 +01:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Hostname(pub String);
impl Display for Hostname {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
2024-04-08 08:22:23 +01:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct User(pub String);
impl Display for User {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug)]
pub struct ApiToken(pub secrecy::Secret<String>);
impl From<String> for ApiToken {
fn from(value: String) -> Self {
Self(value.into())
2024-04-08 11:48:35 +01:00
}
}
impl ExposeSecret<String> for ApiToken {
fn expose_secret(&self) -> &String {
self.0.expose_secret()
}
}
#[derive(Clone, Debug)]
pub struct ForgeDetails {
pub name: ForgeName,
pub forge_type: ForgeType,
pub hostname: Hostname,
pub user: User,
pub token: ApiToken,
// API Token
// Private SSH Key Path
}
impl From<(&ForgeName, &Forge)> for ForgeDetails {
fn from(forge: (&ForgeName, &Forge)) -> Self {
Self {
name: forge.0.clone(),
forge_type: forge.1.forge_type.clone(),
hostname: forge.1.hostname(),
user: forge.1.user(),
token: forge.1.token(),
}
}
}
2024-04-08 08:22:23 +01:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RepoName(pub String);
impl Display for RepoName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
2024-04-08 08:22:23 +01:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RepoPath(pub String);
impl Display for RepoPath {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
2024-04-08 08:22:23 +01:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BranchName(pub String);
impl Display for BranchName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug)]
pub struct RepoDetails {
pub name: RepoName,
pub repo: RepoPath,
pub branch: BranchName,
pub forge: ForgeDetails,
}
impl RepoDetails {
pub fn new(name: &RepoName, repo: &Repo, forge_name: &ForgeName, forge: &Forge) -> Self {
Self {
name: name.clone(),
repo: RepoPath(repo.repo.clone()),
branch: BranchName(repo.branch.clone()),
forge: ForgeDetails {
name: forge_name.clone(),
forge_type: forge.forge_type.clone(),
hostname: forge.hostname(),
user: forge.user(),
token: forge.token(),
},
}
}
}
impl Display for RepoDetails {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}/{} ({}): {}:{}/{} @ {}",
self.forge.name,
self.name,
self.forge.forge_type,
self.forge.hostname,
self.forge.user,
self.repo,
self.branch,
)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
2024-04-07 16:09:16 +01:00
pub enum ForgeType {
2024-04-07 13:47:39 +01:00
ForgeJo,
// Gitea,
// GitHub,
// GitLab,
// BitBucket,
}
impl Display for ForgeType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
2024-04-07 13:47:39 +01:00
#[cfg(test)]
mod tests {
2024-04-07 13:47:39 +01:00
use crate::filesystem::FileSystem;
use super::*;
#[test]
fn test_server_config_load() -> Result<(), OneOf<(std::io::Error, toml::de::Error)>> {
2024-04-07 13:47:39 +01:00
let fs = FileSystem::new_temp().map_err(OneOf::new)?;
fs.write_file(
"git-next-server.toml",
r#"
[forge.default]
forge_type = "ForgeJo"
hostname = "git.example.net"
2024-04-07 13:47:39 +01:00
user = "Bob"
token = "API-Token"
[forge.default.repos]
hello = { repo = "user/hello", branch = "main" }
world = { repo = "user/world", branch = "master" }
2024-04-07 13:47:39 +01:00
"#,
)
.map_err(OneOf::new)?;
let config = ServerConfig::load(&fs)?;
let expected = ServerConfig {
forge: HashMap::from([(
"default".to_string(),
Forge {
forge_type: ForgeType::ForgeJo,
hostname: "git.example.net".to_string(),
user: "Bob".to_string(),
token: "API-Token".to_string(),
repos: HashMap::from([
(
"hello".to_string(),
Repo {
repo: "user/hello".to_string(),
branch: "main".to_string(),
},
),
(
"world".to_string(),
Repo {
repo: "user/world".to_string(),
branch: "master".to_string(),
},
),
]),
},
)]),
};
assert_eq!(config, expected);
2024-04-07 13:47:39 +01:00
Ok(())
}
2024-04-08 09:54:20 +01:00
#[test]
fn test_repo_config_load() -> Result<(), OneOf<(toml::de::Error,)>> {
let toml = r#"
[branches]
main = "main"
next = "next"
dev = "dev"
[options]
"#;
let config = RepoConfig::load(toml)?;
assert_eq!(
config,
RepoConfig {
branches: RepoBranches {
main: "main".to_string(),
next: "next".to_string(),
dev: "dev".to_string(),
},
}
);
Ok(())
}
2024-04-07 13:47:39 +01:00
}