quickcfg/
system.rs

1//! Things to do.
2
3use crate::{
4    environment as e, git, packages, state::State, Data, Facts, FileSystem, Opts, SystemUnit,
5    Timestamp, UnitAllocator, UnitId,
6};
7use anyhow::Error;
8use directories::BaseDirs;
9use serde::Deserialize;
10use std::collections::HashMap;
11use std::fmt;
12use std::path::Path;
13
14#[macro_use]
15mod macros;
16mod copy_dir;
17mod download;
18mod download_and_run;
19mod from_db;
20mod git_sync;
21mod install;
22mod link;
23mod link_dir;
24mod only_for;
25
26use self::copy_dir::CopyDir;
27use self::download::Download;
28use self::download_and_run::DownloadAndRun;
29use self::from_db::FromDb;
30use self::git_sync::GitSync;
31use self::install::Install;
32use self::link::Link;
33use self::link_dir::LinkDir;
34use self::only_for::OnlyFor;
35
36/// What should happen after a system has been translated.
37pub enum Translation<'a> {
38    /// Keep the current system.
39    Keep,
40    /// Discard the current system.
41    Discard,
42    /// Expand and discard the current system into the given collection of systems.
43    Expand(&'a [System]),
44}
45
46macro_rules! system_impl {
47    ($($name:ident,)*) => {
48        impl System {
49            pub fn translate(&self) -> Translation<'_> {
50                use self::System::*;
51
52                match self {
53                    $($name(system) => system.translate(),)*
54                }
55            }
56
57            /// Get the id of this system.
58            pub fn id(&self) -> Option<&str> {
59                use self::System::*;
60
61                match self {
62                    $($name(system) => system.id(),)*
63                }
64            }
65
66            /// Get all things that this system depends on.
67            pub fn requires(&self) -> &[String] {
68                use self::System::*;
69
70                match self {
71                    $($name(system) => system.requires(),)*
72                }
73            }
74
75            /// Apply changes for this system.
76            #[allow(unused)]
77            pub fn apply<E>(&self, input: $crate::system::SystemInput<E>)
78                -> Result<Vec<$crate::system::SystemUnit>, Error>
79            where
80                E: Copy + $crate::environment::Environment,
81            {
82                use anyhow::{Context as _, anyhow};
83                use self::System::*;
84
85                let res = match self {
86                    $($name(system) => system.apply(input),)*
87                };
88
89                res.with_context(|| anyhow!("Failed to run system: {:?}", self))
90            }
91        }
92
93        impl fmt::Display for System {
94            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
95                match *self {
96                    $(
97                    System::$name(ref system) => {
98                        if let Some(id) = system.id() {
99                            write!(fmt, "{}: {}", id, system)
100                        } else {
101                            system.fmt(fmt)
102                        }
103                    }
104                    )*
105                }
106            }
107        }
108    }
109}
110
111#[derive(Deserialize, Debug, PartialEq, Eq)]
112#[serde(tag = "type")]
113pub enum System {
114    #[serde(rename = "copy-dir")]
115    CopyDir(CopyDir),
116    #[serde(rename = "link-dir")]
117    LinkDir(LinkDir),
118    #[serde(rename = "install")]
119    Install(Install),
120    #[serde(rename = "download-and-run")]
121    DownloadAndRun(DownloadAndRun),
122    #[serde(rename = "download")]
123    Download(Download),
124    #[serde(rename = "link")]
125    Link(Link),
126    #[serde(rename = "git-sync")]
127    GitSync(GitSync),
128    #[serde(rename = "only-for")]
129    OnlyFor(OnlyFor),
130    #[serde(rename = "from-db")]
131    FromDb(FromDb),
132}
133
134system_impl![
135    CopyDir,
136    LinkDir,
137    Install,
138    DownloadAndRun,
139    Download,
140    Link,
141    GitSync,
142    OnlyFor,
143    FromDb,
144];
145
146/// All inputs for a system.
147#[derive(Clone, Copy)]
148pub struct SystemInput<'a, 'f, E>
149where
150    E: e::Environment,
151{
152    /// The root directory of the project being built.
153    pub root: &'a Path,
154    /// Known base directories to use.
155    pub base_dirs: Option<&'a BaseDirs>,
156    /// Set of facts.
157    pub facts: &'a Facts,
158    /// Data loaded from hierarchy.
159    pub data: &'a Data,
160    /// Source of environment variables.
161    pub environment: E,
162    /// Detected primary package manager for the system.
163    pub packages: &'a packages::Provider,
164    /// Unit allocator to use.
165    pub allocator: &'a UnitAllocator,
166    /// File utilities.
167    pub file_system: &'a FileSystem<'f>,
168    /// State accessor.
169    pub state: &'a State<'a>,
170    /// Current time.
171    pub now: Timestamp,
172    /// Current optsion.
173    pub opts: &'a Opts,
174    /// The current git system.
175    pub git_system: &'a dyn git::GitSystem,
176}
177
178/// Helper structure used to resolve dependencies.
179#[derive(Default)]
180pub enum Dependency<'a> {
181    /// Transitive dependency, where we have to look up other systems to fully resolve.
182    Transitive(&'a [String]),
183    /// Direct dependency to another unit.
184    Direct(UnitId),
185    /// No dependencies.
186    #[default]
187    None,
188}
189
190impl<'a> Dependency<'a> {
191    /// Resolve all unit dependencies for the current dependency.
192    pub fn resolve(
193        &self,
194        systems: &HashMap<&'a str, Dependency<'a>>,
195    ) -> impl IntoIterator<Item = crate::unit::Dependency> {
196        use std::collections::VecDeque;
197
198        let mut ids = Vec::new();
199
200        let mut queue = VecDeque::new();
201        queue.push_back(self);
202
203        while let Some(dependency) = queue.pop_front() {
204            match *dependency {
205                Dependency::Transitive(requires) => {
206                    for id in requires {
207                        queue.extend(systems.get(id.as_str()));
208                    }
209                }
210                Dependency::Direct(id) => ids.push(crate::unit::Dependency::Unit(id)),
211                Dependency::None => continue,
212            }
213        }
214
215        ids
216    }
217}