autocrate/src/config/mod.rs

204 lines
5 KiB
Rust
Raw Normal View History

2024-01-25 22:33:48 +01:00
use std::{collections::HashMap, fmt::Debug, fs::File, io::BufReader, path::PathBuf};
2024-01-24 23:04:01 +01:00
use git2;
2024-01-25 22:33:48 +01:00
use libpt::log::{debug, error, trace};
2024-01-24 23:04:01 +01:00
use serde::Deserialize;
use url::Url;
2024-01-24 23:04:01 +01:00
use crate::error::*;
2024-01-24 23:04:01 +01:00
pub mod cli;
use cli::Cli;
2024-01-24 23:04:01 +01:00
2024-01-27 23:26:49 +01:00
pub trait YamlConfigSection: Debug + Clone + for<'a> Deserialize<'a> {
fn check(&self) -> Result<()>;
}
2024-01-24 23:04:01 +01:00
#[derive(Debug, Clone, Deserialize)]
pub struct Changelog {
2024-01-27 23:26:49 +01:00
pub enable: bool,
2024-01-25 21:37:52 +00:00
#[serde(alias = "git-log")]
2024-01-27 23:26:49 +01:00
pub git_log: bool,
2024-01-24 23:04:01 +01:00
}
2024-01-27 23:26:49 +01:00
impl YamlConfigSection for Changelog {
fn check(&self) -> Result<()> {
Ok(())
}
2024-01-24 23:04:01 +01:00
}
#[derive(Debug, Clone, Deserialize)]
2024-01-27 23:26:49 +01:00
pub struct UseCargo {
pub publish: bool,
pub registries: Vec<String>,
2024-01-24 23:04:01 +01:00
}
2024-01-27 23:26:49 +01:00
impl YamlConfigSection for UseCargo {
fn check(&self) -> Result<()> {
Ok(())
}
}
2024-01-24 23:04:01 +01:00
#[derive(Debug, Clone, Deserialize)]
2024-01-27 23:26:49 +01:00
pub struct Uses {
cargo: UseCargo,
}
impl YamlConfigSection for Uses {
fn check(&self) -> Result<()> {
self.cargo.check()?;
Ok(())
}
2024-01-25 22:33:48 +01:00
}
2024-01-27 23:26:49 +01:00
#[derive(Debug, Clone, Deserialize)]
pub struct ApiAuth {
pub user: String,
pub pass: Option<String>,
pub pass_file: Option<PathBuf>,
}
impl YamlConfigSection for ApiAuth {
fn check(&self) -> Result<()> {
2024-01-25 22:33:48 +01:00
if self.pass.is_some() && self.pass_file.is_some() {
let err = ConfigError::YamlApiAuthBothPass(self.clone()).into();
error!("{err}");
return Err(err);
}
2024-01-27 23:26:49 +01:00
if self.pass_file.is_some() {
let file = self.pass_file.clone().unwrap();
if !file.exists() {
return Err(ConfigError::PassFileDoesNotExist(file).into());
}
}
2024-01-25 22:33:48 +01:00
Ok(())
}
}
2024-01-24 23:04:01 +01:00
#[derive(Debug, Clone, Deserialize)]
pub struct Api {
2024-02-16 20:00:55 +01:00
pub server_type: ApiType,
2024-01-27 23:26:49 +01:00
pub endpoint: Url,
2024-01-25 22:33:48 +01:00
/// May be left empty if the Api does not need auth or the auth is part of the
/// [endpoint](Api::endpoint) [Url].
2024-01-27 23:26:49 +01:00
pub auth: Option<ApiAuth>,
2024-01-25 22:33:48 +01:00
}
2024-01-27 23:26:49 +01:00
impl YamlConfigSection for Api {
fn check(&self) -> Result<()> {
2024-02-16 20:00:55 +01:00
self.server_type.check()?;
2024-01-27 23:26:49 +01:00
match self.endpoint.socket_addrs(|| None) {
Ok(_) => (),
Err(err) => return Err(err.into()),
}
if self.auth.is_some() {
self.auth.clone().unwrap().check()?;
2024-01-25 22:33:48 +01:00
}
Ok(())
}
2024-01-24 23:04:01 +01:00
}
#[derive(Debug, Clone, Deserialize)]
pub enum ApiType {
2024-01-25 22:33:48 +01:00
#[serde(alias = "gitea")]
2024-01-24 23:04:01 +01:00
Gitea,
2024-01-25 22:33:48 +01:00
#[serde(alias = "gitlab")]
2024-01-24 23:04:01 +01:00
Gitlab,
2024-01-25 22:33:48 +01:00
#[serde(alias = "github", alias = "GitHub")]
2024-01-24 23:04:01 +01:00
Github,
}
2024-01-27 23:26:49 +01:00
impl YamlConfigSection for ApiType {
fn check(&self) -> Result<()> {
Ok(())
}
}
2024-01-24 23:04:01 +01:00
#[derive(Debug, Clone, Deserialize)]
pub struct YamlConfig {
pub changelog: Changelog,
pub uses: Uses,
2024-01-25 22:33:48 +01:00
pub api: HashMap<String, Api>,
}
2024-01-27 23:26:49 +01:00
impl YamlConfigSection for YamlConfig {
fn check(&self) -> Result<()> {
self.changelog.check()?;
self.uses.check()?;
for api in self.api.values() {
api.check()?;
}
Ok(())
}
}
2024-01-25 22:33:48 +01:00
impl YamlConfig {
/// check if the built configuration is valid
pub fn check(&self) -> Result<()> {
for api in &self.api {
api.1.check()?;
}
Ok(())
}
2024-01-24 23:04:01 +01:00
}
pub struct Config {
pub yaml: YamlConfig,
2024-01-27 23:26:49 +01:00
pub cli: Cli,
2024-01-24 23:04:01 +01:00
pub repo: git2::Repository,
pub path: PathBuf,
}
impl Debug for Config {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
format_args!(
2024-01-24 23:04:01 +01:00
"Config {{yaml: {:?}, repo_path: {:?}}}",
self.yaml, self.path
)
)
}
}
impl Config {
2024-02-16 18:58:20 +01:00
pub fn load(cli: &Cli) -> Result<Self> {
2024-01-24 23:04:01 +01:00
let repo = match git2::Repository::open_from_env() {
Ok(repo) => repo,
2024-01-25 21:37:52 +00:00
Err(_err) => {
2024-01-25 22:33:48 +01:00
let err = ConfigError::GitRepoNotFound.into();
error!("{err}");
return Err(err);
2024-01-24 23:04:01 +01:00
}
};
let mut path = repo.path().to_path_buf();
path.pop(); // we want the real root, not the `.git` dir
let yaml_file_name = if path.join(".autocrate.yaml").exists() {
".autocrate.yaml"
} else if path.join(".autocrate.yml").exists() {
".autocrate.yml"
} else {
2024-01-25 22:33:48 +01:00
let err = ConfigError::NoYamlFile.into();
error!("{err}");
return Err(err);
};
let yaml_file_path = path.join(yaml_file_name);
// we can be sure it exists from the checks above
assert!(yaml_file_path.exists());
if !yaml_file_path.is_file() {
2024-01-25 22:33:48 +01:00
let err = ConfigError::YamlFileIsNotFile.into();
error!("{err}");
return Err(err);
}
let yaml_rd = BufReader::new(File::open(yaml_file_path)?);
2024-01-25 22:33:48 +01:00
debug!("reading yaml config and building data structure");
let yaml: YamlConfig = serde_yaml::from_reader(yaml_rd)?;
2024-01-25 22:33:48 +01:00
trace!("load config:\n{:#?}", yaml);
yaml.check()?;
debug!("built and checked yaml config");
2024-01-27 23:26:49 +01:00
Ok(Config {
yaml,
repo,
path,
2024-02-16 18:58:20 +01:00
cli: cli.clone(),
2024-01-27 23:26:49 +01:00
})
2024-01-24 23:04:01 +01:00
}
}