libside/builder/
apply.rs

1use super::MinimalContext;
2use crate::apply::SystemState;
3use crate::requirements::Requirement;
4use crate::system::System;
5use crate::{
6    graph::{ApplyResult, Graph, Pending},
7    StateDirs,
8};
9use std::path::{Path, PathBuf};
10
11pub struct PreparedBuild<'d, R> {
12    contexts: Vec<MinimalContext>,
13    install: &'d StateDirs,
14    target_graph: Graph<R, Pending>,
15}
16
17impl<'d, R: Requirement> PreparedBuild<'d, R> {
18    pub fn new(
19        install: &'d StateDirs,
20        contexts: Vec<MinimalContext>,
21        graph: Graph<R, Pending>,
22    ) -> Self {
23        PreparedBuild {
24            contexts,
25            install,
26            target_graph: graph,
27        }
28    }
29
30    pub fn generate_files<'r, S: System>(
31        &self,
32        system: &mut S,
33        _prev: &SystemState<R>,
34    ) -> Result<&Graph<R, Pending>, ()> {
35        // TODO: Use system to create the files
36        self.install.create_dirs(system).unwrap();
37
38        // Generate the config files, because we need them for the install
39        for config in self.contexts.iter().map(|c| c.files.iter()).flatten() {
40            let path = config.source.parent().unwrap();
41            println!("  prep : {}", config.source.display());
42            system.make_dir_all(&path).unwrap();
43            system
44                .put_file_contents(&config.source, &config.contents)
45                .unwrap();
46        }
47
48        for deleted in self
49            .contexts
50            .iter()
51            .map(|c| c.deleted_files.iter())
52            .flatten()
53        {
54            let path = deleted.save_to.parent().unwrap();
55            println!("  prep : {}", path.display());
56            system.make_dir_all(&path).unwrap();
57        }
58
59        // Create the main application files
60        // TODO: Should we allow custom owners for exposed files, or should we keep everything owned by root? Does it even matter if we don't need the files to ever be writeable?
61        for context in self.contexts.iter() {
62            for exposed in context.exposed.iter() {
63                println!("  expose: {:?}", exposed.source);
64                let metadata = exposed.source.symlink_metadata().unwrap();
65                if metadata.file_type().is_dir() {
66                    system.make_dir_all(&exposed.target).unwrap();
67                    copy(system, &exposed.source, &exposed.target).unwrap();
68                } else {
69                    copy_file(system, &exposed.source, &exposed.target).unwrap();
70                }
71            }
72        }
73
74        Ok(&self.target_graph)
75    }
76
77    pub fn save<S: System>(
78        self,
79        system: &mut S,
80        result: ApplyResult,
81    ) -> Result<SystemState<R>, ()> {
82        let state = SystemState {
83            graph: self.target_graph.apply_execution_results(result),
84        };
85
86        self.install.write_dbs(system, &state).unwrap();
87        Ok(state)
88    }
89}
90
91pub fn copy<S: System, U: AsRef<Path>, V: AsRef<Path>>(
92    system: &mut S,
93    from: U,
94    to: V,
95) -> Result<(), S::Error> {
96    let mut stack = Vec::new();
97    stack.push(PathBuf::from(from.as_ref()));
98
99    let output_root = PathBuf::from(to.as_ref());
100    let input_root = PathBuf::from(from.as_ref()).components().count();
101
102    while let Some(working_path) = stack.pop() {
103        // Generate a relative path
104        let src: PathBuf = working_path.components().skip(input_root).collect();
105
106        // Create a destination if missing
107        let dest = if src.components().count() == 0 {
108            output_root.clone()
109        } else {
110            output_root.join(&src)
111        };
112
113        if !system.path_exists(&dest)? {
114            system.make_dir_all(&dest)?;
115        }
116
117        for entry in system.read_dir(&working_path)? {
118            let path = working_path.join(entry);
119            if path.is_dir() {
120                stack.push(path);
121            } else {
122                match path.file_name() {
123                    Some(filename) => {
124                        let dest_path = dest.join(filename);
125                        copy_file(system, &path, &dest_path)?;
126                    }
127                    None => {
128                        panic!("failed to copy: {:?}", path);
129                    }
130                }
131            }
132        }
133    }
134
135    Ok(())
136}
137
138fn copy_file<S: System>(system: &mut S, path: &PathBuf, dest: &PathBuf) -> Result<(), S::Error> {
139    // TODO: Handle symlinks, permissions
140    Ok(system.copy_file(&path, &dest)?)
141}