openfare_lib/extension/
process.rs

1use anyhow::{format_err, Context, Result};
2
3use super::commands;
4use super::common;
5
6#[derive(Debug, serde::Serialize, serde::Deserialize)]
7pub struct StaticData {
8    pub name: String,
9    pub registry_host_names: Vec<String>,
10    pub version: String,
11}
12
13#[derive(Debug, Clone)]
14pub struct ProcessExtension {
15    process_path_: std::path::PathBuf,
16    name_: String,
17    registry_host_names_: Vec<String>,
18    version_: String,
19}
20
21impl common::FromProcess for ProcessExtension {
22    fn from_process(
23        process_path: &std::path::PathBuf,
24        extension_config_path: &std::path::PathBuf,
25    ) -> Result<Self>
26    where
27        Self: Sized,
28    {
29        let static_data: StaticData = if extension_config_path.is_file() {
30            let file = std::fs::File::open(&extension_config_path)?;
31            let reader = std::io::BufReader::new(file);
32            serde_json::from_reader(reader)?
33        } else {
34            let static_data: Box<StaticData> = run_process(&process_path, &vec!["static-data"])?;
35            let static_data = *static_data;
36
37            let file = std::fs::OpenOptions::new()
38                .write(true)
39                .create(true)
40                .open(&extension_config_path)
41                .context(format!(
42                    "Can't open/create file for writing: {}",
43                    extension_config_path.display()
44                ))?;
45            let writer = std::io::BufWriter::new(file);
46            serde_json::to_writer(writer, &static_data)?;
47            static_data
48        };
49
50        Ok(ProcessExtension {
51            process_path_: process_path.clone(),
52            name_: static_data.name,
53            registry_host_names_: static_data.registry_host_names,
54            version_: static_data.version,
55        })
56    }
57}
58
59impl common::Extension for ProcessExtension {
60    fn name(&self) -> String {
61        self.name_.clone()
62    }
63
64    fn registries(&self) -> Vec<String> {
65        self.registry_host_names_.clone()
66    }
67
68    fn version(&self) -> String {
69        self.version_.clone()
70    }
71
72    fn package_dependencies_locks(
73        &self,
74        package_name: &str,
75        package_version: &Option<&str>,
76        extension_args: &Vec<String>,
77    ) -> Result<commands::package_dependencies_locks::PackageDependenciesLocks> {
78        let mut args = vec![
79            super::commands::package_dependencies_locks::COMMAND_NAME,
80            "--package-name",
81            package_name,
82        ];
83        if let Some(package_version) = package_version {
84            args.push("--package-version");
85            args.push(package_version);
86        }
87        for extension_arg in extension_args {
88            args.push("--extension-args");
89            args.push(extension_arg);
90        }
91        let output: Box<commands::package_dependencies_locks::PackageDependenciesLocks> =
92            run_process(&self.process_path_, &args)?;
93        Ok(*output)
94    }
95
96    /// Returns a list of local package dependencies specification files.
97    fn project_dependencies_locks(
98        &self,
99        working_directory: &std::path::PathBuf,
100        extension_args: &Vec<String>,
101    ) -> Result<commands::project_dependencies_locks::ProjectDependenciesLocks> {
102        let working_directory = working_directory.to_str().ok_or(format_err!(
103            "Failed to parse path into string: {}",
104            working_directory.display()
105        ))?;
106        let mut args = vec![
107            super::commands::project_dependencies_locks::COMMAND_NAME,
108            "--working-directory",
109            working_directory,
110        ];
111        for extension_arg in extension_args {
112            args.push("--extension-args");
113            args.push(extension_arg);
114        }
115
116        let output: Box<commands::project_dependencies_locks::ProjectDependenciesLocks> =
117            run_process(&self.process_path_, &args)?;
118        Ok(*output)
119    }
120}
121
122#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
123pub struct ProcessResult<T> {
124    pub ok: Option<T>,
125    pub err: Option<String>,
126}
127
128fn run_process<'a, T: ?Sized>(process_path: &std::path::PathBuf, args: &Vec<&str>) -> Result<Box<T>>
129where
130    for<'de> T: serde::Deserialize<'de> + 'a,
131{
132    log::debug!(
133        "Executing extensions process call with arguments\n{:?}",
134        args
135    );
136    let process = process_path.to_str().ok_or(format_err!(
137        "Failed to parse string from process path: {}",
138        process_path.display()
139    ))?;
140    let handle = std::process::Command::new(process)
141        .args(args)
142        .stdin(std::process::Stdio::null())
143        .stderr(std::process::Stdio::piped())
144        .stdout(std::process::Stdio::piped())
145        .output()?;
146
147    let stdout = String::from_utf8_lossy(&handle.stdout);
148    let stdout = stdout.to_string();
149
150    let result = hex::decode(&stdout)?.clone();
151    let process_result: ProcessResult<T> =
152        bincode::deserialize(&result).expect("deserialize result with bincode");
153
154    if let Some(result) = process_result.ok {
155        Ok(Box::new(result))
156    } else if let Some(result) = process_result.err {
157        Err(format_err!(result))
158    } else {
159        Err(format_err!("Failed to find ok or err result from process."))
160    }
161}