use anyhow::Context;
use bson::{Document, doc};
use derive_builder::Builder;
use derive_default_builder::DefaultBuilder;
use partial_derive2::Partial;
use serde::{Deserialize, Serialize};
use strum::Display;
use typeshare::typeshare;
use crate::{
deserializers::{
env_vars_deserializer, option_env_vars_deserializer,
option_string_list_deserializer, string_list_deserializer,
},
entities::I64,
};
use super::{
EnvironmentVar, SystemCommand, environment_vars_from_str,
resource::{Resource, ResourceListItem, ResourceQuery},
};
#[typeshare]
pub type RepoListItem = ResourceListItem<RepoListItemInfo>;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct RepoListItemInfo {
pub server_id: String,
pub builder_id: String,
pub last_pulled_at: I64,
pub last_built_at: I64,
pub git_provider: String,
pub repo: String,
pub branch: String,
pub repo_link: String,
pub state: RepoState,
pub cloned_hash: Option<String>,
pub cloned_message: Option<String>,
pub built_hash: Option<String>,
pub latest_hash: Option<String>,
}
#[typeshare]
#[derive(
Debug, Clone, Copy, Default, Serialize, Deserialize, Display,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum RepoState {
#[default]
Unknown,
Ok,
Failed,
Cloning,
Pulling,
Building,
}
#[cfg(feature = "utoipa")]
#[derive(utoipa::ToSchema)]
#[schema(as = Repo)]
pub struct RepoSchema(
#[schema(inline)] pub Resource<RepoConfig, RepoInfo>,
);
#[typeshare]
pub type Repo = Resource<RepoConfig, RepoInfo>;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct RepoInfo {
#[serde(default)]
pub last_pulled_at: I64,
#[serde(default)]
pub last_built_at: I64,
pub built_hash: Option<String>,
pub built_message: Option<String>,
pub latest_hash: Option<String>,
pub latest_message: Option<String>,
}
#[typeshare(serialized_as = "Partial<RepoConfig>")]
pub type _PartialRepoConfig = PartialRepoConfig;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct RepoConfig {
#[serde(default, alias = "server")]
#[partial_attr(serde(alias = "server"))]
#[builder(default)]
pub server_id: String,
#[serde(default, alias = "builder")]
#[partial_attr(serde(alias = "builder"))]
#[builder(default)]
pub builder_id: String,
#[serde(default = "default_git_provider")]
#[builder(default = "default_git_provider()")]
#[partial_default(default_git_provider())]
pub git_provider: String,
#[serde(default = "default_git_https")]
#[builder(default = "default_git_https()")]
#[partial_default(default_git_https())]
pub git_https: bool,
#[serde(default)]
#[builder(default)]
pub git_account: String,
#[serde(default)]
#[builder(default)]
pub repo: String,
#[serde(default = "default_branch")]
#[builder(default = "default_branch()")]
#[partial_default(default_branch())]
pub branch: String,
#[serde(default)]
#[builder(default)]
pub commit: String,
#[serde(default)]
#[builder(default)]
pub path: String,
#[serde(default = "default_webhook_enabled")]
#[builder(default = "default_webhook_enabled()")]
#[partial_default(default_webhook_enabled())]
pub webhook_enabled: bool,
#[serde(default)]
#[builder(default)]
pub webhook_secret: String,
#[serde(default)]
#[builder(default)]
pub on_clone: SystemCommand,
#[serde(default)]
#[builder(default)]
pub on_pull: SystemCommand,
#[serde(default, deserialize_with = "string_list_deserializer")]
#[partial_attr(serde(
default,
deserialize_with = "option_string_list_deserializer"
))]
#[builder(default)]
pub links: Vec<String>,
#[serde(default, deserialize_with = "env_vars_deserializer")]
#[partial_attr(serde(
default,
deserialize_with = "option_env_vars_deserializer"
))]
#[builder(default)]
pub environment: String,
#[serde(default = "default_env_file_path")]
#[builder(default = "default_env_file_path()")]
#[partial_default(default_env_file_path())]
pub env_file_path: String,
#[serde(default)]
#[builder(default)]
pub skip_secret_interp: bool,
}
impl RepoConfig {
pub fn builder() -> RepoConfigBuilder {
RepoConfigBuilder::default()
}
pub fn env_vars(&self) -> anyhow::Result<Vec<EnvironmentVar>> {
environment_vars_from_str(&self.environment)
.context("Invalid environment")
}
}
fn default_git_provider() -> String {
String::from("github.com")
}
fn default_git_https() -> bool {
true
}
fn default_branch() -> String {
String::from("main")
}
fn default_env_file_path() -> String {
String::from(".env")
}
fn default_webhook_enabled() -> bool {
true
}
impl Default for RepoConfig {
fn default() -> Self {
Self {
server_id: Default::default(),
builder_id: Default::default(),
git_provider: default_git_provider(),
git_https: default_git_https(),
repo: Default::default(),
branch: default_branch(),
commit: Default::default(),
git_account: Default::default(),
path: Default::default(),
on_clone: Default::default(),
on_pull: Default::default(),
links: Default::default(),
environment: Default::default(),
env_file_path: default_env_file_path(),
skip_secret_interp: Default::default(),
webhook_enabled: default_webhook_enabled(),
webhook_secret: Default::default(),
}
}
}
#[cfg(feature = "utoipa")]
impl utoipa::PartialSchema for PartialRepoConfig {
fn schema()
-> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::schema!(#[inline] std::collections::HashMap<String, serde_json::Value>).into()
}
}
#[cfg(feature = "utoipa")]
impl utoipa::ToSchema for PartialRepoConfig {}
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct RepoActionState {
pub cloning: bool,
pub pulling: bool,
pub building: bool,
pub renaming: bool,
}
#[typeshare]
pub type RepoQuery = ResourceQuery<RepoQuerySpecifics>;
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Default, DefaultBuilder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct RepoQuerySpecifics {
pub repos: Vec<String>,
}
impl super::resource::AddFilters for RepoQuerySpecifics {
fn add_filters(&self, filters: &mut Document) {
if !self.repos.is_empty() {
filters.insert("config.repo", doc! { "$in": &self.repos });
}
}
}