use crate::{
environment as e, git, packages, state::State, Data, Facts, FileSystem, Opts, SystemUnit,
Timestamp, UnitAllocator, UnitId,
};
use anyhow::Error;
use directories::BaseDirs;
use serde::Deserialize;
use std::collections::HashMap;
use std::fmt;
use std::path::Path;
#[macro_use]
mod macros;
mod copy_dir;
mod download;
mod download_and_run;
mod from_db;
mod git_sync;
mod install;
mod link;
mod link_dir;
mod only_for;
use self::copy_dir::CopyDir;
use self::download::Download;
use self::download_and_run::DownloadAndRun;
use self::from_db::FromDb;
use self::git_sync::GitSync;
use self::install::Install;
use self::link::Link;
use self::link_dir::LinkDir;
use self::only_for::OnlyFor;
pub enum Translation<'a> {
Keep,
Discard,
Expand(&'a [System]),
}
macro_rules! system_impl {
($($name:ident,)*) => {
impl System {
pub fn translate(&self) -> Translation<'_> {
use self::System::*;
match self {
$($name(system) => system.translate(),)*
}
}
pub fn id(&self) -> Option<&str> {
use self::System::*;
match self {
$($name(system) => system.id(),)*
}
}
pub fn requires(&self) -> &[String] {
use self::System::*;
match self {
$($name(system) => system.requires(),)*
}
}
#[allow(unused)]
pub fn apply<E>(&self, input: $crate::system::SystemInput<E>)
-> Result<Vec<$crate::system::SystemUnit>, Error>
where
E: Copy + $crate::environment::Environment,
{
use anyhow::{Context as _, anyhow};
use self::System::*;
let res = match self {
$($name(system) => system.apply(input),)*
};
res.with_context(|| anyhow!("Failed to run system: {:?}", self))
}
}
impl fmt::Display for System {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
$(
System::$name(ref system) => {
if let Some(id) = system.id() {
write!(fmt, "{}: {}", id, system)
} else {
system.fmt(fmt)
}
}
)*
}
}
}
}
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
#[serde(tag = "type")]
pub enum System {
#[serde(rename = "copy-dir")]
CopyDir(CopyDir),
#[serde(rename = "link-dir")]
LinkDir(LinkDir),
#[serde(rename = "install")]
Install(Install),
#[serde(rename = "download-and-run")]
DownloadAndRun(DownloadAndRun),
#[serde(rename = "download")]
Download(Download),
#[serde(rename = "link")]
Link(Link),
#[serde(rename = "git-sync")]
GitSync(GitSync),
#[serde(rename = "only-for")]
OnlyFor(OnlyFor),
#[serde(rename = "from-db")]
FromDb(FromDb),
}
system_impl![
CopyDir,
LinkDir,
Install,
DownloadAndRun,
Download,
Link,
GitSync,
OnlyFor,
FromDb,
];
#[derive(Clone, Copy)]
pub struct SystemInput<'a, 'f, E>
where
E: e::Environment,
{
pub root: &'a Path,
pub base_dirs: Option<&'a BaseDirs>,
pub facts: &'a Facts,
pub data: &'a Data,
pub environment: E,
pub packages: &'a packages::Provider,
pub allocator: &'a UnitAllocator,
pub file_system: &'a FileSystem<'f>,
pub state: &'a State<'a>,
pub now: Timestamp,
pub opts: &'a Opts,
pub git_system: &'a dyn git::GitSystem,
}
#[derive(Default)]
pub enum Dependency<'a> {
Transitive(&'a [String]),
Direct(UnitId),
#[default]
None,
}
impl<'a> Dependency<'a> {
pub fn resolve(
&self,
systems: &HashMap<&'a str, Dependency<'a>>,
) -> impl IntoIterator<Item = crate::unit::Dependency> {
use std::collections::VecDeque;
let mut ids = Vec::new();
let mut queue = VecDeque::new();
queue.push_back(self);
while let Some(dependency) = queue.pop_front() {
match *dependency {
Dependency::Transitive(requires) => {
for id in requires {
queue.extend(systems.get(id.as_str()));
}
}
Dependency::Direct(id) => ids.push(crate::unit::Dependency::Unit(id)),
Dependency::None => continue,
}
}
ids
}
}