A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

397 lines
9.9 KiB

use crate::artifacts::{
DatabaseName as DatabaseNameArtifact, Path as PathArtifact, ServiceName as ServiceNameArtifact,
UserName as UserNameArtifact,
};
use crate::templates::php::FpmPoolConfig;
use std::hash::Hash;
use std::path::Path;
pub trait Resource {
type Artifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Key<D>(pub D);
impl<D> Resource for Key<D> {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Csr<D>(pub D);
impl<D> Resource for Csr<D> {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Cert<D>(pub D);
impl<D> Resource for Cert<D> {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct CertChain<D>(pub D);
impl<D> Resource for CertChain<D> {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct KeyAndCertBundle<D>(pub D);
impl<D> Resource for KeyAndCertBundle<D> {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct File<P>(pub P, pub Rc<str>);
impl<P> Resource for File<P> {
type Artifact = ();
}
impl File<Rc<Path>> {
pub fn new(p: impl Into<Rc<Path>>, content: impl AsRef<str>) -> Self {
Self(p.into(), content.as_ref().into())
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct GitCheckout<'a, P>(pub P, pub &'a str, pub &'a str);
impl<'a, P> Resource for GitCheckout<'a, P> {
type Artifact = ();
}
impl<'a> GitCheckout<'a, Rc<Path>> {
pub fn new(target: impl Into<Rc<Path>>, src: &'a str, head: &'a str) -> Self {
Self(target.into(), src, head)
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Dir<P>(pub P);
impl<P> Resource for Dir<P> {
type Artifact = ();
}
impl Dir<Rc<Path>> {
pub fn new(p: impl AsRef<Path>) -> Self {
Self(p.as_ref().into())
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct UserForDomain<D>(pub D);
impl<D> Resource for UserForDomain<D> {
type Artifact = (UserNameArtifact, PathArtifact);
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct AcmeAccountKey;
impl Resource for AcmeAccountKey {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct AcmeUser;
impl Resource for AcmeUser {
type Artifact = (UserNameArtifact, PathArtifact);
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct AcmeRootCert;
impl Resource for AcmeRootCert {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct AcmeChallengesDir;
impl Resource for AcmeChallengesDir {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct AcmeChallengesNginxSnippet;
impl Resource for AcmeChallengesNginxSnippet {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct StoredDirectory<P>(pub &'static str, pub P);
impl<P> Resource for StoredDirectory<P> {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct LoadedDirectory<P>(pub &'static str, pub P);
impl<P> Resource for LoadedDirectory<P> {
type Artifact = PathArtifact;
}
pub fn get_saved_directory(
storage_name: &'static str,
target: impl Into<Rc<Path>>,
) -> (StoredDirectory<Rc<Path>>, LoadedDirectory<Rc<Path>>) {
let target = target.into();
(
StoredDirectory(storage_name, target.clone()),
LoadedDirectory(storage_name, target),
)
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct User(pub Rc<str>);
impl Resource for User {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct SystemdSocketService<D, P>(pub D, pub &'static str, pub P, pub P, pub bool);
impl<D, P> Resource for SystemdSocketService<D, P> {
type Artifact = (PathArtifact, PathArtifact, UserNameArtifact);
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct NpmInstall<P>(pub P);
impl<P> Resource for NpmInstall<P> {
type Artifact = ();
}
impl NpmInstall<Rc<Path>> {
pub fn new(path: impl Into<Rc<Path>>) -> Self {
Self(path.into())
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Owner<P>(pub Rc<str>, pub P);
impl<P> Resource for Owner<P> {
type Artifact = ();
}
impl Owner<Rc<Path>> {
pub fn new(user: &UserNameArtifact, p: impl Into<Rc<Path>>) -> Self {
Self(user.0.clone(), p.into())
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct ServeCustom<D>(pub D, pub Rc<str>);
impl<D> Resource for ServeCustom<D> {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct ServePhp<D, P, C>(pub D, pub P, pub &'static str, pub Rc<str>, pub C);
impl<D, P, C> Resource for ServePhp<D, P, C> {
type Artifact = PathArtifact;
}
// Domain, service name, exec, static, dir
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct ServeService<D, P>(pub D, pub &'static str, pub P, pub P, pub P, pub bool);
impl<D, P> Resource for ServeService<D, P> {
type Artifact = PathArtifact;
}
impl<D> ServeService<D, Rc<Path>> {
pub fn new(
domain: D,
service_name: &'static str,
exec: impl Into<Rc<Path>>,
static_path: impl Into<Rc<Path>>,
working_directory: impl Into<Rc<Path>>,
is_nodejs: bool,
) -> Self {
Self(
domain,
service_name,
exec.into(),
static_path.into(),
working_directory.into(),
is_nodejs,
)
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct ServeRedir<D>(pub D, pub D);
impl<D> Resource for ServeRedir<D> {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct ServeStatic<D, P>(pub D, pub P);
impl<D, P> Resource for ServeStatic<D, P> {
type Artifact = PathArtifact;
}
impl<D> ServeStatic<D, Rc<Path>> {
pub fn new(domain: D, path: impl Into<Rc<Path>>) -> Self {
Self(domain, path.into())
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct DefaultServer;
impl Resource for DefaultServer {
type Artifact = PathArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct PhpFpmPool<D>(pub D, pub FpmPoolConfig);
impl<D> Resource for PhpFpmPool<D> {
type Artifact = (
PathArtifact,
PathArtifact,
UserNameArtifact,
ServiceNameArtifact,
);
}
// A single domain could possibly need multiple databases
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct MariaDbDatabase<D>(pub D);
impl<D> Resource for MariaDbDatabase<D> {
type Artifact = (DatabaseNameArtifact, UserNameArtifact, PathArtifact);
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct MariaDbUser<D>(pub D);
impl<D> Resource for MariaDbUser<D> {
type Artifact = UserNameArtifact;
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct PostgresqlDatabase<D>(pub D);
impl<D> Resource for PostgresqlDatabase<D> {
type Artifact = (DatabaseNameArtifact, PathArtifact);
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct WordpressPlugin<P>(pub P, pub &'static str);
impl<P> Resource for WordpressPlugin<P> {
type Artifact = ();
}
impl WordpressPlugin<Rc<Path>> {
pub fn new(path: impl Into<Rc<Path>>, name: &'static str) -> Self {
Self(path.into(), name)
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct WordpressTranslation<P>(pub P, pub &'static str, pub &'static str);
impl<P> Resource for WordpressTranslation<P> {
type Artifact = ();
}
impl WordpressTranslation<Rc<Path>> {
pub fn new(path: impl Into<Rc<Path>>, version: &'static str, lang: &'static str) -> Self {
Self(path.into(), version, lang)
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Cron<D>(pub D, pub Rc<str>);
impl<D> Resource for Cron<D> {
type Artifact = ();
}
use std::rc::{Rc, Weak};
pub trait FromResource<R> {
fn from_resource(from: &Rc<R>) -> (Self, Weak<R>)
where
Self: Sized;
}
pub trait FromArtifact<R: Resource> {
fn from_artifact(from: R::Artifact) -> Self
where
Self: Sized;
fn into_artifact(self) -> R::Artifact
where
Self: Sized;
}
macro_rules! default_resources {
( $($name:ident: $type:ty,)* ) => {
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum DefaultResources<'a, D> {
$( $name(Rc<$type>) ),*
}
$(impl<'a, D> FromResource<$type> for DefaultResources<'a, D> {
fn from_resource(inner: &Rc<$type>) -> (Self, Weak<$type>) {
(Self::$name(Rc::clone(&inner)), Rc::downgrade(&inner))
}
})*
#[derive(Clone, Debug)]
pub enum DefaultArtifacts<'a, D> {
$( $name(<$type as Resource>::Artifact) ),*
}
$(impl<'a, D> FromArtifact<$type> for DefaultArtifacts<'a, D> {
fn from_artifact(from: <$type as Resource>::Artifact) -> Self {
Self::$name(from)
}
fn into_artifact(self) -> <$type as Resource>::Artifact {
match self {
Self::$name(inner) => inner,
_ => panic!()
}
}
})*
}
}
// Only one enum entry per resource type, otherwise the equality checks fail
default_resources!(
AcmeAccountKey: AcmeAccountKey,
AcmeChallengesDir: AcmeChallengesDir,
AcmeChallengesNginxSnippet: AcmeChallengesNginxSnippet,
AcmeRootCert: AcmeRootCert,
AcmeUser: AcmeUser,
Cert: Cert<D>,
CertChain: CertChain<D>,
Cron: Cron<D>,
Csr: Csr<D>,
DefaultServer: DefaultServer,
Dir: Dir<Rc<Path>>,
File: File<Rc<Path>>,
GitCheckout: GitCheckout<'a, Rc<Path>>,
Key: Key<D>,
KeyAndCertBundle: KeyAndCertBundle<D>,
LoadedDirectory: LoadedDirectory<Rc<Path>>,
MariaDbDatabase: MariaDbDatabase<D>,
MariaDbUser: MariaDbUser<D>,
PostgresqlDatabase: PostgresqlDatabase<D>,
SystemdSocketService: SystemdSocketService<D, Rc<Path>>,
NpmInstall: NpmInstall<Rc<Path>>,
Owner: Owner<Rc<Path>>,
PhpFpmPool: PhpFpmPool<D>,
ServeCustom: ServeCustom<D>,
ServeService: ServeService<D, Rc<Path>>,
ServePhp: ServePhp<D, Rc<Path>, FpmPoolConfig>,
ServeRedir: ServeRedir<D>,
ServeStatic: ServeStatic<D, Rc<Path>>,
StoredDirectory: StoredDirectory<Rc<Path>>,
User: User,
UserForDomain: UserForDomain<D>,
WordpressPlugin: WordpressPlugin<Rc<Path>>,
WordpressTranslation: WordpressTranslation<Rc<Path>>,
);
pub fn serve_php<D, C: Into<FpmPoolConfig>>(
domain: D,
path: impl Into<Rc<Path>>,
root_filename: &'static str,
nginx_config: impl Into<Rc<str>>,
pool_config: C,
) -> ServePhp<D, Rc<Path>, FpmPoolConfig> {
ServePhp(
domain,
path.into(),
root_filename,
nginx_config.into(),
pool_config.into(),
)
}