use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use color_eyre::eyre::eyre; pub use models::PackageId; use serde::{Deserialize, Serialize}; use url::Url; use super::git_hash::GitHash; use crate::action::Actions; use crate::backup::BackupActions; use crate::config::action::ConfigActions; use crate::dependencies::Dependencies; use crate::migration::Migrations; use crate::net::interface::Interfaces; use crate::prelude::*; use crate::procedure::docker::DockerContainers; use crate::procedure::PackageProcedure; use crate::status::health_check::HealthChecks; use crate::util::serde::Regex; use crate::util::Version; use crate::version::{Current, VersionT}; use crate::volume::Volumes; use crate::Error; fn current_version() -> Version { Current::new().semver().into() } #[derive(Clone, Debug, Deserialize, Serialize, HasModel)] #[serde(rename_all = "kebab-case")] #[model = "Model"] pub struct Manifest { #[serde(default = "current_version")] pub eos_version: Version, pub id: PackageId, #[serde(default)] pub git_hash: Option, pub title: String, pub version: Version, pub description: Description, #[serde(default)] pub assets: Assets, #[serde(default)] pub build: Option>, pub release_notes: String, pub license: String, // type of license pub wrapper_repo: Url, pub upstream_repo: Url, pub support_site: Option, pub marketing_site: Option, pub donation_url: Option, #[serde(default)] pub alerts: Alerts, pub main: PackageProcedure, pub health_checks: HealthChecks, pub config: Option, pub properties: Option, pub volumes: Volumes, // #[serde(default)] pub interfaces: Interfaces, // #[serde(default)] pub backup: BackupActions, #[serde(default)] pub migrations: Migrations, #[serde(default)] pub actions: Actions, // #[serde(default)] // pub permissions: Permissions, #[serde(default)] pub dependencies: Dependencies, pub containers: Option, #[serde(default)] pub replaces: Vec, #[serde(default)] pub hardware_requirements: HardwareRequirements, } impl Manifest { pub fn package_procedures(&self) -> impl Iterator { use std::iter::once; let main = once(&self.main); let cfg_get = self.config.as_ref().map(|a| &a.get).into_iter(); let cfg_set = self.config.as_ref().map(|a| &a.set).into_iter(); let props = self.properties.iter(); let backups = vec![&self.backup.create, &self.backup.restore].into_iter(); let migrations = self .migrations .to .values() .chain(self.migrations.from.values()); let actions = self.actions.0.values().map(|a| &a.implementation); main.chain(cfg_get) .chain(cfg_set) .chain(props) .chain(backups) .chain(migrations) .chain(actions) } pub fn with_git_hash(mut self, git_hash: GitHash) -> Self { self.git_hash = Some(git_hash); self } } #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct HardwareRequirements { #[serde(default)] device: BTreeMap, ram: Option, pub arch: Option>, } #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct Assets { #[serde(default)] pub license: Option, #[serde(default)] pub instructions: Option, #[serde(default)] pub icon: Option, #[serde(default)] pub docker_images: Option, #[serde(default)] pub assets: Option, #[serde(default)] pub scripts: Option, } impl Assets { pub fn license_path(&self) -> &Path { self.license .as_ref() .map(|a| a.as_path()) .unwrap_or(Path::new("LICENSE.md")) } pub fn instructions_path(&self) -> &Path { self.instructions .as_ref() .map(|a| a.as_path()) .unwrap_or(Path::new("INSTRUCTIONS.md")) } pub fn icon_path(&self) -> &Path { self.icon .as_ref() .map(|a| a.as_path()) .unwrap_or(Path::new("icon.png")) } pub fn icon_type(&self) -> &str { self.icon .as_ref() .and_then(|icon| icon.extension()) .and_then(|ext| ext.to_str()) .unwrap_or("png") } pub fn docker_images_path(&self) -> &Path { self.docker_images .as_ref() .map(|a| a.as_path()) .unwrap_or(Path::new("docker-images")) } pub fn assets_path(&self) -> &Path { self.assets .as_ref() .map(|a| a.as_path()) .unwrap_or(Path::new("assets")) } pub fn scripts_path(&self) -> &Path { self.scripts .as_ref() .map(|a| a.as_path()) .unwrap_or(Path::new("scripts")) } } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Description { pub short: String, pub long: String, } impl Description { pub fn validate(&self) -> Result<(), Error> { if self.short.chars().skip(160).next().is_some() { return Err(Error::new( eyre!("Short description must be 160 characters or less."), crate::ErrorKind::ValidateS9pk, )); } if self.long.chars().skip(5000).next().is_some() { return Err(Error::new( eyre!("Long description must be 5000 characters or less."), crate::ErrorKind::ValidateS9pk, )); } Ok(()) } } #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct Alerts { pub install: Option, pub uninstall: Option, pub restore: Option, pub start: Option, pub stop: Option, }