_origen/utility/
results.rs

1use origen_metal::{Outcome, OutcomeSubtypes, Result, TypedValue};
2use pyapi_metal::prelude::typed_value;
3use pyapi_metal::runtime_error;
4use pyo3::prelude::*;
5use pyo3::types::{PyDict, PyType};
6
7#[macro_export]
8macro_rules! incomplete_result_error {
9    ($result_type:expr) => {{
10        crate::runtime_error!(format!(
11            "Incomplete or Uninitialized {} encountered",
12            $result_type
13        ))?
14    }};
15}
16
17pub fn define(py: Python, m: &PyModule) -> PyResult<()> {
18    let subm = PyModule::new(py, "results")?;
19    subm.add_class::<BuildResult>()?;
20    subm.add_class::<UploadResult>()?;
21    subm.add_class::<ExecResult>()?;
22    m.add_submodule(subm)?;
23    Ok(())
24}
25
26/// Generic build result
27#[pyclass(subclass)]
28pub struct BuildResult {
29    // Origen Build Result
30    pub build_result: Option<Outcome>,
31}
32
33#[pymethods]
34impl BuildResult {
35    #[classmethod]
36    #[pyo3(signature=(instance, succeeded, build_contents=None, message=None, metadata=None))]
37    fn __init__(
38        _cls: &PyType,
39        instance: &PyAny,
40        succeeded: bool,
41        build_contents: Option<Vec<String>>,
42        message: Option<String>,
43        metadata: Option<&PyDict>,
44    ) -> PyResult<()> {
45        let mut i = instance.extract::<PyRefMut<Self>>()?;
46        let mut o = Outcome::new_success_or_fail(succeeded);
47        o.subtype = Some(OutcomeSubtypes::BuildResult);
48        o.message = message;
49        o.metadata = typed_value::from_optional_pydict(metadata)?;
50        o.insert_keyword_result("build_contents", build_contents);
51        i.build_result = Some(o);
52        Ok(())
53    }
54
55    #[new]
56    fn new() -> Self {
57        Self { build_result: None }
58    }
59
60    #[getter]
61    fn succeeded(&self) -> PyResult<bool> {
62        Ok(self.build_result()?.succeeded())
63    }
64
65    #[getter]
66    fn failed(&self) -> PyResult<bool> {
67        Ok(!self.succeeded()?)
68    }
69
70    #[getter]
71    fn build_contents(&self) -> PyResult<Option<Vec<String>>> {
72        match self
73            .build_result()?
74            .require_keyword_result("build_contents")?
75        {
76            TypedValue::None => Ok(None),
77            TypedValue::Vec(v) => Ok(Some(
78                v.iter()
79                    .map(|i| i.as_string())
80                    .collect::<Result<Vec<String>>>()?,
81            )),
82            _ => runtime_error!(
83                "Cannot extract build contents as either 'None' or as a 'list of strs'"
84            ),
85        }
86    }
87
88    #[getter]
89    fn message(&self) -> PyResult<Option<String>> {
90        Ok(self.build_result()?.message.clone())
91    }
92
93    #[getter]
94    fn metadata<'py>(&self, py: Python<'py>) -> PyResult<Option<&'py PyDict>> {
95        typed_value::into_optional_pydict(py, self.build_result()?.metadata.as_ref())
96    }
97}
98
99impl BuildResult {
100    pub fn build_result(&self) -> PyResult<&Outcome> {
101        match self.build_result.as_ref() {
102            Some(r) => Ok(r),
103            None => return crate::incomplete_result_error!("Build Result"),
104        }
105    }
106
107    pub fn to_py(py: Python, build_result: &Outcome) -> PyResult<Py<Self>> {
108        Py::new(
109            py,
110            Self {
111                build_result: Some(build_result.clone()),
112            },
113        )
114    }
115}
116
117/// Generic upload result
118#[pyclass(subclass)]
119pub struct UploadResult {
120    // Origen Upload Result
121    pub upload_result: Option<Outcome>,
122}
123
124#[pymethods]
125impl UploadResult {
126    #[classmethod]
127    #[pyo3(signature=(instance, succeeded, message=None, metadata=None))]
128    fn __init__(
129        _cls: &PyType,
130        instance: &PyAny,
131        succeeded: bool,
132        message: Option<String>,
133        metadata: Option<&PyDict>,
134    ) -> PyResult<()> {
135        let mut i = instance.extract::<PyRefMut<Self>>()?;
136        let mut o = Outcome::new_success_or_fail(succeeded);
137        o.subtype = Some(OutcomeSubtypes::UploadResult);
138        o.message = message;
139        o.metadata = typed_value::from_optional_pydict(metadata)?;
140        i.upload_result = Some(o);
141        Ok(())
142    }
143
144    #[new]
145    fn new() -> Self {
146        Self {
147            upload_result: None,
148        }
149    }
150}
151
152impl UploadResult {
153    pub fn upload_result(&self) -> PyResult<&Outcome> {
154        match self.upload_result.as_ref() {
155            Some(r) => Ok(r),
156            None => crate::incomplete_result_error!("Upload Result"),
157        }
158    }
159}
160
161#[pyclass]
162pub struct ExecResult {
163    pub exec_result: Option<Outcome>,
164}
165
166#[pymethods]
167impl ExecResult {
168    #[classmethod]
169    #[pyo3(signature=(instance, exit_code, stdout=None, stderr=None))]
170    fn __init__(
171        _cls: &PyType,
172        instance: &PyAny,
173        exit_code: i32,
174        stdout: Option<Vec<String>>,
175        stderr: Option<Vec<String>>,
176    ) -> PyResult<()> {
177        let mut i = instance.extract::<PyRefMut<Self>>()?;
178        let mut o = Outcome::new_success_or_fail(exit_code == 0);
179        o.subtype = Some(OutcomeSubtypes::ExecResult);
180        o.insert_keyword_result("exit_code", exit_code);
181        o.insert_keyword_result("stdout", stdout);
182        o.insert_keyword_result("stderr", stderr);
183        i.exec_result = Some(o);
184        Ok(())
185    }
186
187    #[new]
188    fn new() -> Self {
189        Self { exec_result: None }
190    }
191
192    #[getter]
193    pub fn exit_code(&self) -> PyResult<i32> {
194        Ok(self
195            .exec_result()?
196            .require_keyword_result("exit_code")?
197            .try_into()?)
198    }
199
200    #[getter]
201    pub fn stdout(&self) -> PyResult<Option<Vec<String>>> {
202        Ok(
203            match self
204                .exec_result()?
205                .require_keyword_result("stdout")?
206                .as_option()
207            {
208                Some(out_lines) => Some(out_lines.try_into()?),
209                None => None,
210            },
211        )
212    }
213
214    #[getter]
215    pub fn stderr(&self) -> PyResult<Option<Vec<String>>> {
216        Ok(
217            match self
218                .exec_result()?
219                .require_keyword_result("stderr")?
220                .as_option()
221            {
222                Some(err_lines) => Some(err_lines.try_into()?),
223                None => None,
224            },
225        )
226    }
227
228    pub fn succeeded(&self) -> PyResult<bool> {
229        Ok(self.exec_result()?.succeeded())
230    }
231
232    pub fn failed(&self) -> PyResult<bool> {
233        Ok(self.exec_result()?.failed())
234    }
235}
236
237impl ExecResult {
238    fn exec_result(&self) -> PyResult<&Outcome> {
239        match self.exec_result.as_ref() {
240            Some(r) => Ok(r),
241            None => crate::incomplete_result_error!("Exec Result"),
242        }
243    }
244}