1#[allow(unused_imports)]
2#[macro_use]
3extern crate origen;
4#[macro_use]
5extern crate origen_metal;
6
7use pyapi_metal;
8
9#[macro_use]
10mod macros;
11
12mod current_command;
13mod dut;
14mod extensions;
15mod file_handler;
16mod infrastructure;
17mod meta;
18mod model;
19#[macro_use]
20mod pins;
21mod registers;
22mod services;
23#[macro_use]
24mod timesets;
25mod _frontend;
26mod _helpers;
27mod application;
28mod producer;
29mod prog_gen;
30mod standard_sub_blocks;
31pub mod tester;
32#[macro_use]
33mod utility;
34mod plugins;
35
36use crate::registers::bit_collection::BitCollection;
37use num_bigint::BigUint;
38use om::lazy_static::lazy_static;
39use origen::core::status::DependencySrc;
40use origen::{clean_target, Dut, Error, Operation, Result, Value, ORIGEN_CONFIG, STATUS, TEST};
41use origen_metal as om;
42use origen_metal::FLOW;
43use paste::paste;
44use pyapi_metal::{pypath, runtime_error};
45use pyo3::prelude::*;
46use pyo3::types::{PyAny, PyBytes, PyDict};
47use pyo3::wrap_pyfunction;
48use std::path::{Path, PathBuf};
49use std::str::FromStr;
50use std::sync::MutexGuard;
51use utility::location::Location;
52
53pub mod built_info {
54 include!(concat!(env!("OUT_DIR"), "/built.rs"));
56}
57
58#[pymodule]
59fn _origen(py: Python, m: &PyModule) -> PyResult<()> {
61 m.add_wrapped(wrap_pyfunction!(initialize))?;
62 m.add_wrapped(wrap_pyfunction!(status))?;
63 m.add_wrapped(wrap_pyfunction!(version))?;
64 m.add_wrapped(wrap_pyfunction!(config))?;
65 m.add_wrapped(wrap_pyfunction!(config_metadata))?;
66 m.add_wrapped(wrap_pyfunction!(app_config))?;
67 m.add_wrapped(wrap_pyfunction!(is_app_present))?;
68 m.add_wrapped(wrap_pyfunction!(clean_mode))?;
69 m.add_wrapped(wrap_pyfunction!(target_file))?;
70 m.add_wrapped(wrap_pyfunction!(test))?;
71 m.add_wrapped(wrap_pyfunction!(test_ast))?;
72 m.add_wrapped(wrap_pyfunction!(flow))?;
73 m.add_wrapped(wrap_pyfunction!(flow_ast))?;
74 m.add_wrapped(wrap_pyfunction!(output_directory))?;
75 m.add_wrapped(wrap_pyfunction!(website_output_directory))?;
76 m.add_wrapped(wrap_pyfunction!(website_source_directory))?;
77 m.add_wrapped(wrap_pyfunction!(prepare_for_target_load))?;
78 m.add_wrapped(wrap_pyfunction!(start_new_test))?;
79 m.add_wrapped(wrap_pyfunction!(unhandled_error_count))?;
80 m.add_wrapped(wrap_pyfunction!(set_output_dir))?;
81 m.add_wrapped(wrap_pyfunction!(set_reference_dir))?;
82 m.add_wrapped(wrap_pyfunction!(exit_pass))?;
83 m.add_wrapped(wrap_pyfunction!(exit_fail))?;
84 m.add_wrapped(wrap_pyfunction!(enable_debug))?;
85 m.add_wrapped(wrap_pyfunction!(set_operation))?;
86 m.add_wrapped(wrap_pyfunction!(boot_users))?;
87
88 dut::define(py, m)?;
89 tester::define(py, m)?;
90 application::define(py, m)?;
91 producer::define(py, m)?;
92 services::define(py, m)?;
93 utility::define(py, m)?;
94 standard_sub_blocks::define(py, m)?;
95 prog_gen::define(py, m)?;
96 file_handler::define(py, m)?;
97 plugins::define(py, m)?;
98 extensions::define(py, m)?;
99 current_command::define(py, m)?;
100 infrastructure::define(py, m)?;
101
102 pyapi_metal::define(py, m)?;
105 m.setattr(current_command::ATTR_NAME, py.None())?;
106 Ok(())
107}
108
109fn extract_value<'a>(
110 bits_or_val: &PyAny,
111 size: Option<u32>,
112 dut: &'a MutexGuard<Dut>,
113) -> Result<Value<'a>> {
114 let bits = bits_or_val.extract::<PyRef<BitCollection>>();
115 if bits.is_ok() {
116 return Ok(Value::Bits(bits.unwrap().materialize(dut)?, size));
117 }
118 let value = bits_or_val.extract::<BigUint>();
119 if value.is_ok() {
120 return match size {
121 Some(x) => Ok(Value::Data(value.unwrap(), x)),
122 None => Err(Error::new(
123 "A size argument must be supplied along with a data value",
124 )),
125 };
126 }
127 Err(Error::new("Illegal bits/value argument"))
128}
129
130fn unpack_transaction_options(
133 trans: &mut origen::Transaction,
134 kwargs: Option<&PyDict>,
135) -> PyResult<()> {
136 if let Some(opts) = kwargs {
137 if let Some(address) = opts.get_item("address")? {
138 trans.address = Some(address.extract::<BigUint>()?);
139 }
140 if let Some(w) = opts.get_item("address_width")? {
141 trans.address_width = Some(w.extract::<usize>()?);
142 }
143 if let Some(_mask) = opts.get_item("mask")? {
144 panic!("option not supported yet!");
145 }
146 if let Some(_overlay) = opts.get_item("overlay")? {
147 panic!("option not supported yet!");
148 }
149 if let Some(_overlay_str) = opts.get_item("overlay_str")? {
150 panic!("option not supported yet!");
151 }
152 }
153 Ok(())
154}
155
156fn unpack_capture_kwargs(
157 dut: &origen::Dut,
158 cap_trans: &mut origen::Capture,
159 kwargs: Option<&PyDict>,
160 pins_allowed: bool,
161 cycles_allowed: bool,
162) -> PyResult<()> {
163 if let Some(opts) = kwargs {
164 if let Some(sym) = opts.get_item("symbol")? {
165 cap_trans.symbol = Some(sym.extract::<String>()?);
166 }
167 if let Some(enables) = opts.get_item("mask")? {
168 cap_trans.enables = Some(enables.extract::<BigUint>()?);
169 }
170 if let Some(cycles) = opts.get_item("cycles")? {
171 if cycles_allowed {
172 cap_trans.cycles = Some(cycles.extract::<usize>()?);
173 } else {
174 return runtime_error!("'cycles' capture option is not valid in this context");
175 }
176 }
177 if let Some(pins) = opts.get_item("pins")? {
178 if pins_allowed {
179 let pins_vec = pins.extract::<Vec<&PyAny>>()?;
180 cap_trans.pin_ids = Some(pins::vec_to_ppin_ids(&dut, pins_vec)?);
181 } else {
182 return runtime_error!("'pins' capture option is not valid in this context");
183 }
184 }
185 }
186 Ok(())
187}
188
189fn unpack_transaction_kwargs(trans: &mut origen::Transaction, kwargs: &PyDict) -> PyResult<()> {
192 if let Some(mask) = kwargs.get_item("mask")? {
193 if let Ok(big_mask) = mask.extract::<num_bigint::BigUint>() {
194 trans.bit_enable = big_mask;
195 } else {
196 return crate::type_error!("Could not extract kwarg 'mask' as an integer");
197 }
198 }
199 if let Some(overlay) = kwargs.get_item("overlay")? {
200 let overlay_mask;
201 let overlay_symbol;
202 let overlay_cycles;
203 if let Some(mask) = kwargs.get_item("overlay_mask")? {
204 if let Ok(big_mask) = mask.extract::<num_bigint::BigUint>() {
205 overlay_mask = Some(big_mask);
206 } else {
207 return crate::type_error!("Could not extract kwarg 'overlay_mask' as an integer");
208 }
209 } else {
210 if let Some(ovl) = trans.overlay.as_ref() {
211 overlay_mask = ovl.enables.clone();
212 } else {
213 overlay_mask = None;
214 }
215 }
216 if let Some(s) = kwargs.get_item("overlay_symbol")? {
217 if let Ok(sym) = s.extract::<String>() {
218 overlay_symbol = Some(sym);
219 } else {
220 return crate::type_error!("Could not extract kwarg 'overlay_symbol' as a String");
221 }
222 } else {
223 if let Some(ovl) = trans.overlay.as_ref() {
224 overlay_symbol = ovl.symbol.clone();
225 } else {
226 overlay_symbol = None;
227 }
228 }
229 if let Some(c) = kwargs.get_item("overlay_cycles")? {
230 if let Ok(i) = c.extract::<usize>() {
231 overlay_cycles = Some(i);
232 } else {
233 return crate::type_error!(
234 "Could not extract kwarg 'overlay_cycles' as an Integer"
235 );
236 }
237 } else {
238 if let Some(ovl) = trans.overlay.as_ref() {
239 overlay_cycles = ovl.cycles.clone();
240 } else {
241 overlay_cycles = None;
242 }
243 }
244 if let Ok(should_overlay) = overlay.extract::<bool>() {
245 if should_overlay {
246 trans.apply_overlay(None, overlay_symbol, overlay_mask)?;
248 }
249 } else if let Ok(overlay_name) = overlay.extract::<String>() {
250 trans.apply_overlay(Some(overlay_name), overlay_symbol, overlay_mask)?;
251 if overlay_cycles.is_some() {
252 trans.overlay.as_mut().unwrap().cycles = overlay_cycles;
253 }
254 } else {
255 return crate::type_error!(
256 "Could not extract kwarg 'overlay' as either a bool or a string"
257 );
258 }
259 }
260 Ok(())
261}
262
263fn resolve_transaction(
268 dut: &std::sync::MutexGuard<origen::Dut>,
269 trans: &PyAny,
270 action: Option<origen::TransactionAction>,
271 kwargs: Option<&PyDict>,
272) -> PyResult<origen::Transaction> {
273 let mut width = 32;
274 if let Some(opts) = kwargs {
275 if let Some(w) = opts.get_item("width")? {
276 width = w.extract::<u32>()?;
277 }
278 }
279 let value = extract_value(trans, Some(width), &dut)?;
280 let mut trans;
281 if let Some(a) = action {
282 match a {
283 origen::TransactionAction::Write => trans = value.to_write_transaction(&dut)?,
284 origen::TransactionAction::Verify => trans = value.to_verify_transaction(&dut)?,
285 origen::TransactionAction::Capture => {
286 trans = value.to_capture_transaction(&dut)?;
287 unpack_capture_kwargs(
288 &dut,
289 &mut trans.capture.as_mut().unwrap(),
290 kwargs,
291 false,
292 false,
293 )?;
294 return Ok(trans);
295 }
296 _ => {
297 return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!(
298 "Resolving transactions for {:?} is not supported",
299 a
300 )))
301 }
302 }
303 } else {
304 trans = value.to_write_transaction(&dut)?;
305 trans.action = None;
306 }
307
308 if let Some(opts) = kwargs {
309 if let Some(address) = opts.get_item("address")? {
310 if !address.is_none() {
311 trans.address = Some(address.extract::<BigUint>()?);
312 }
313 }
314 if let Some(w) = opts.get_item("address_width")? {
315 trans.address_width = Some(w.extract::<usize>()?);
316 }
317 if let Some(_mask) = opts.get_item("mask")? {
318 panic!("option not supported yet!");
319 }
320 if let Some(_overlay) = opts.get_item("overlay")? {
321 panic!("option not supported yet!");
322 }
323 if let Some(_overlay_str) = opts.get_item("overlay_str")? {
324 panic!("option not supported yet!");
325 }
326 }
327 Ok(trans)
328}
329
330#[pyfunction]
332fn exit_fail() -> PyResult<()> {
333 exit_fail!();
334}
335
336#[pyfunction]
338fn exit_pass() -> PyResult<()> {
339 exit_pass!();
340}
341
342fn origen_mod_path() -> PyResult<PathBuf> {
343 Python::with_gil(|py| {
344 let locals = PyDict::new(py);
345 locals.set_item("importlib", py.import("importlib")?)?;
346 let p = PathBuf::from(
347 py.eval(
348 "importlib.util.find_spec('_origen').origin",
349 None,
350 Some(&locals),
351 )?
352 .extract::<String>()?,
353 );
354 Ok(p.parent().unwrap().to_path_buf())
355 })
356}
357
358#[pyfunction]
360#[pyo3(signature=(log_verbosity, verbosity_keywords, cli_location, cli_version, fe_pkg_loc, fe_exe_loc, invocation))]
361fn initialize(
362 py: Python,
363 log_verbosity: Option<u8>,
364 verbosity_keywords: Vec<String>,
365 cli_location: Option<String>,
366 cli_version: Option<String>,
367 fe_pkg_loc: Option<PathBuf>,
368 fe_exe_loc: Option<PathBuf>,
369 invocation: Option<(String, Option<PathBuf>)>,
370) -> PyResult<()> {
371 origen::initialize(
372 log_verbosity,
373 verbosity_keywords,
374 cli_location,
375 cli_version,
376 fe_pkg_loc,
377 fe_exe_loc,
378 );
379 origen::STATUS.update_other_build_info("pyapi_version", built_info::PKG_VERSION)?;
380 if let Some(invoc) = invocation {
381 match DependencySrc::try_from(invoc) {
382 Ok(d) => origen::STATUS.set_dependency_src(Some(d)),
383 Err(e) => log_error!("{}", e.to_string()),
384 }
385 }
386 origen::FRONTEND
387 .write()
388 .unwrap()
389 .set_frontend(Box::new(_frontend::Frontend::new()))?;
390
391 if let Some(app) = &STATUS.app {
392 origen::STATUS.set_in_origen_core_app(origen_mod_path()? == app.root);
393 } else {
394 origen::STATUS.set_in_origen_core_app(false);
395 }
396
397 use crate::pyapi_metal::prelude::frontend::*;
398 with_mut_py_data_stores(|py, mut py_ds| {
399 let pymod = PyModule::import(py, "_origen")?;
401 py_ds.add_category(
402 py,
403 "ldaps",
404 Some(pymod.getattr("utility")?.getattr("boot_ldaps")?.into()),
405 Some(true),
406 )?;
407 Ok(())
408 })?;
409 boot_users(py)?;
410 match origen::setup_sessions() {
411 Ok(_) => {}
412 Err(e) => log_error!(
413 "Failed to setup user and application sessions. Received error: \n{}",
414 e
415 ),
416 }
417 Ok(())
418}
419
420#[pyfunction]
421fn set_output_dir(dir: &str) -> PyResult<()> {
423 STATUS.set_output_dir(Path::new(dir));
424 Ok(())
425}
426
427#[pyfunction]
428fn set_reference_dir(dir: &str) -> PyResult<()> {
430 STATUS.set_reference_dir(Path::new(dir));
431 Ok(())
432}
433
434#[pyfunction]
435fn enable_debug() -> PyResult<()> {
437 STATUS.set_debug_enabled(true);
438 Ok(())
439}
440
441#[pyfunction]
442fn set_operation(name: String) -> PyResult<()> {
444 match Operation::from_str(&name) {
445 Ok(op) => {
446 STATUS.set_operation(op);
447 Ok(())
448 }
449 Err(e) => Err(PyErr::from(Error::new(&e))),
450 }
451}
452
453#[pyfunction]
454fn unhandled_error_count() -> PyResult<usize> {
459 Ok(STATUS.unhandled_error_count())
460}
461
462#[pyfunction]
464fn test() -> PyResult<()> {
465 println!("{}", TEST.to_string());
466 Ok(())
467}
468
469#[pyfunction]
471fn test_ast() -> PyResult<Vec<u8>> {
472 Ok(TEST.to_pickle())
473}
474
475#[pyfunction]
477fn flow() -> PyResult<()> {
478 println!("{}", FLOW.to_string());
479 Ok(())
480}
481
482#[pyfunction]
484fn flow_ast() -> PyResult<Vec<u8>> {
485 Ok(FLOW.to_pickle())
486}
487
488#[pyfunction]
491fn status(py: Python) -> PyResult<PyObject> {
492 let ret = PyDict::new(py);
493 let _ = ret.set_item("is_app_present", &STATUS.is_app_present);
495 if let Some(app) = origen::app() {
496 let _ = ret.set_item("root", format!("{}", app.root.display()));
497 }
498 let _ = ret.set_item("origen_version", &STATUS.origen_version.to_string());
499 let _ = ret.set_item("home", format!("{}", STATUS.home.display()));
500 let _ = ret.set_item("on_windows", om::running_on_windows());
501 ret.set_item(
502 "origen_core_support_version",
503 STATUS.origen_core_support_version.to_string(),
504 )?;
505 ret.set_item(
506 "origen_metal_backend_version",
507 STATUS.origen_metal_backend_version.to_string(),
508 )?;
509 ret.set_item(
510 "other_build_info",
511 pyapi_metal::_helpers::map_to_pydict(py, &mut STATUS.other_build_info().iter())?,
512 )?;
513 ret.set_item(
514 "cli_version",
515 match STATUS.cli_version() {
516 Some(v) => Some(v.to_string()).to_object(py),
517 None => py.None(),
518 },
519 )?;
520 ret.set_item(
521 "cli_location",
522 match STATUS.cli_location() {
523 Some(path) => pypath!(py, path.display()),
524 None => py.None(),
525 },
526 )?;
527 ret.set_item(
528 "is_app_in_origen_dev_mode",
529 STATUS.is_app_in_origen_dev_mode,
530 )?;
531 ret.set_item("in_origen_core_app", STATUS.in_origen_core_app())?;
532
533 infrastructure::pyproject::populate_status(py, ret)?;
535 Ok(ret.into())
536}
537
538#[pyfunction]
540fn version() -> PyResult<String> {
541 Ok(
542 origen_metal::utils::version::Version::new_pep440(&STATUS.origen_version.to_string())?
543 .to_string(),
544 )
545}
546
547#[pyfunction]
549fn config(py: Python) -> PyResult<PyObject> {
550 let ret = PyDict::new(py);
551 let _ = ret.set_item("python_cmd", &ORIGEN_CONFIG.python_cmd);
553 let _ = ret.set_item("pkg_server", &ORIGEN_CONFIG.pkg_server);
554 let _ = ret.set_item("pkg_server_push", &ORIGEN_CONFIG.pkg_server_push);
555 let _ = ret.set_item("pkg_server_pull", &ORIGEN_CONFIG.pkg_server_pull);
556 let _ = ret.set_item("some_val", &ORIGEN_CONFIG.some_val);
557 Ok(ret.into())
558}
559
560#[pyfunction]
561fn config_metadata<'py>(py: Python<'py>) -> PyResult<&'py PyDict> {
562 let m = origen::origen_config_metadata();
563 let retn = PyDict::new(py);
564 retn.set_item(
565 "files",
566 m.files
567 .iter()
568 .map(|p| Ok(pypath!(py, p.display())))
569 .collect::<PyResult<Vec<PyObject>>>()?,
570 )?;
571 Ok(retn)
572}
573
574#[pyfunction]
575fn is_app_present() -> PyResult<bool> {
576 Ok(STATUS.is_app_present)
577}
578
579#[pyfunction]
581fn app_config(py: Python) -> PyResult<Option<PyObject>> {
582 if let Some(app) = origen::app() {
583 let ret = PyDict::new(py);
584 let _ = app.with_config(|config| {
585 let _ = ret.set_item("name", &config.name);
586 let _ = ret.set_item("target", &config.target);
587 let _ = ret.set_item("mode", &config.mode);
588 let _ = ret.set_item("__output_directory__", &config.output_directory);
589 let _ = ret.set_item(
590 "__website_output_directory__",
591 &config.website_output_directory,
592 );
593 let _ = ret.set_item(
594 "__website_source_directory__",
595 &config.website_source_directory,
596 );
597 let _ = ret.set_item(
598 "website_release_location",
599 match &config.website_release_location {
600 Some(loc) => Py::new(
601 py,
602 Location {
603 location: (*loc).clone(),
604 },
605 )
606 .unwrap()
607 .to_object(py),
608 None => py.None(),
609 },
610 );
611 let _ = ret.set_item("website_release_name", &config.website_release_name);
612 let _ = ret.set_item("release", &config.release);
613 let program_generation = PyDict::new(py);
614 let _ = program_generation.set_item(
615 "flow_visualization",
616 config.program_generation.flow_visualization,
617 );
618 let _ = ret.set_item("program_generation", program_generation);
619 Ok(())
620 });
621 Ok(Some(ret.into()))
622 } else {
623 Ok(None)
624 }
625}
626
627#[pyfunction]
630fn clean_mode(name: &str) -> PyResult<String> {
631 let c = origen::clean_mode(name);
632 Ok(c)
633}
634
635#[pyfunction]
636fn target_file(name: &str, dir: &str) -> PyResult<String> {
640 let c = clean_target!(name, dir, true);
641 Ok(c)
642}
643
644#[pyfunction]
645fn output_directory(py: Python) -> PyResult<PyObject> {
646 let dir = origen::STATUS.output_dir();
647 Ok(pypath!(py, dir.display()))
648}
649
650#[pyfunction]
651fn website_output_directory(py: Python) -> PyResult<PyObject> {
652 let dir = origen::app().unwrap().website_output_directory();
653 Ok(pypath!(py, dir.display()))
654}
655
656#[pyfunction]
657fn website_source_directory(py: Python) -> PyResult<PyObject> {
658 let dir = origen::app().unwrap().website_source_directory();
659 Ok(pypath!(py, dir.display()))
660}
661
662#[pyfunction]
663fn prepare_for_target_load() -> PyResult<()> {
665 origen::prepare_for_target_load()?;
666 Ok(())
667}
668
669#[pyfunction]
670fn start_new_test(name: Option<String>) -> PyResult<()> {
672 origen::start_new_test(name);
673 Ok(())
674}
675
676pub fn pickle(py: Python, object: &PyAny) -> PyResult<Vec<u8>> {
677 let pickle = PyModule::import(py, "pickle")?;
678 pickle
679 .getattr("dumps")?
680 .call1((object,))?
681 .extract::<Vec<u8>>()
682}
683
684pub fn depickle<'a>(py: Python<'a>, object: &Vec<u8>) -> PyResult<&'a PyAny> {
685 let pickle = PyModule::import(py, "pickle")?;
686 let bytes = PyBytes::new(py, object);
687 pickle.getattr("loads")?.call1((bytes,))
688}
689
690pub fn with_pycallbacks<T, F>(mut func: F) -> PyResult<T>
691where
692 F: FnMut(Python, &PyAny) -> PyResult<T>,
693{
694 Python::with_gil(|py| {
695 let pycallbacks = py.import("origen.callbacks")?;
696 func(py, pycallbacks)
697 })
698}
699
700pub fn get_full_class_name(obj: &PyAny) -> PyResult<String> {
701 let cls = obj.getattr("__class__")?;
702 let mut n = cls.getattr("__module__")?.extract::<String>()?;
703 n.push_str(&format!(
704 ".{}",
705 cls.getattr("__qualname__")?.extract::<String>()?
706 ));
707 Ok(n)
708}
709
710#[pyfunction]
712pub fn boot_users(py: Python) -> PyResult<pyapi_metal::framework::users::Users> {
713 lazy_static! {
714 static ref BASE_MSG: &'static str = "Encountered an error when initializing users";
715 }
716
717 log_trace!("Initializing Users...");
718 if let Some(r) = &ORIGEN_CONFIG.session__user_root {
719 log_trace!("Setting user session root to {}", r);
720 let mut users = om::users_mut();
721 let sc = users.default_session_config_mut();
722 sc.root = Some(PathBuf::from(r));
723 }
724
725 let users = pyapi_metal::framework::users::users()?;
726
727 if let Some(pw_cache_option) = &crate::ORIGEN_CONFIG.user__password_cache_option {
728 match users.set_default_password_cache_option(Some(pw_cache_option)) {
729 Ok(_) => {}
730 Err(e) => {
731 om::log_error!(
732 "{}: Error encountered updating default password cache option",
733 *BASE_MSG
734 );
735 om::log_error!("{}", e);
736 }
737 }
738 }
739
740 if let Some(dsets) = &crate::ORIGEN_CONFIG.user__datasets {
741 let mut replace_default = true;
742 for (dn, config) in dsets {
743 log_trace!("Adding user dataset {}", dn);
744 match config.try_into() {
745 Ok(om_config) => {
746 match pyapi_metal::framework::users::UserDatasetConfig::new_py(py, om_config) {
747 Ok(py_config) => {
748 if replace_default {
749 match users.override_default_dataset(
750 dn,
751 Some(py_config.into_py(py).as_ref(py)),
752 ) {
753 Ok(_) => {
754 replace_default = false;
755 }
756 Err(e) => {
757 om::log_error!("{}: Error encountered updating default dataset with config '{}'", *BASE_MSG, dn);
758 om::log_error!("{}", e);
759 }
760 }
761 } else {
762 match users.add_dataset(
763 dn,
764 Some(
765 pyapi_metal::framework::users::UserDatasetConfig::new_py(
766 py,
767 config.try_into()?,
768 )?
769 .into_py(py)
770 .as_ref(py),
771 ),
772 false,
773 ) {
774 Ok(_) => {}
775 Err(e) => {
776 om::log_error!(
777 "{}: Error encountered adding dataset '{}'",
778 *BASE_MSG,
779 dn
780 );
781 om::log_error!("{}", e);
782 }
783 }
784 }
785 }
786 Err(e) => {
787 om::log_error!(
789 "{}: Error encountered processing dataset config for '{}'",
790 *BASE_MSG,
791 dn
792 );
793 om::log_error!("{}", e);
794 }
795 }
796 }
797 Err(e) => {
798 om::log_error!(
799 "{}: Error encountered processing dataset config for '{}'",
800 *BASE_MSG,
801 dn
802 );
803 om::log_error!("{}", e);
804 }
805 }
806 }
807 }
808
809 if let Some(hierarchy) = &crate::ORIGEN_CONFIG.user__data_lookup_hierarchy {
811 match users.apply_data_lookup_hierarchy(hierarchy.to_owned()) {
812 Ok(_) => {}
813 Err(e) => {
814 om::log_error!(
815 "{}: Error encountered setting the default lookup hierarchy",
816 *BASE_MSG
817 );
818 om::log_error!("{}", e);
819 om::log_error!("Forcing empty dataset lookup hierarchy...");
820 users.apply_data_lookup_hierarchy(vec![])?;
821 }
822 }
823 } else {
824 if crate::ORIGEN_CONFIG.user__datasets.is_some()
825 && crate::ORIGEN_CONFIG.user__datasets.as_ref().unwrap().len() > 1
826 {
827 users.apply_data_lookup_hierarchy(vec![])?;
830 }
831 }
832
833 for (m, ds) in &crate::ORIGEN_CONFIG.user__dataset_motives {
835 match users.add_motive(m, ds, false) {
836 Ok(_) => {}
837 Err(e) => {
838 om::log_error!(
839 "{}: Error encountered adding dataset motive '{}'",
840 *BASE_MSG,
841 m
842 );
843 om::log_error!("{}", e);
844 }
845 }
846 }
847
848 macro_rules! log_error__set_field_for_default_user {
849 ($u:expr, $field:tt, $val:expr, $name:expr ) => {
850 paste! {
851 match $u.[<set_ $field>]($val) {
852 Ok(_) => {},
853 Err(e) => {
854 log_error!("{}: Failed to initialize default user '{}'", *BASE_MSG, $name);
855 log_error!(" Failed to set field '{}'", stringify!($field));
856 log_error!("{}", e);
857 log_error!("Bailing on initializing default user '{}'", $name);
858 continue;
859 }
860 }
861 }
862 };
863 }
864
865 for (name, config) in &crate::ORIGEN_CONFIG.default_users {
867 match users.add(name, config.auto_populate) {
868 Ok(u) => {
869 if let Some(s) = config.should_validate_passwords {
870 log_error__set_field_for_default_user!(
871 u,
872 should_validate_passwords,
873 Some(s),
874 name
875 )
876 }
877 if let Some(uname) = &config.username {
878 log_error__set_field_for_default_user!(
879 u,
880 username,
881 Some(uname.to_owned()),
882 name
883 );
884 }
885 if let Some(pw) = &config.password {
886 log_error__set_field_for_default_user!(u, password, Some(pw.to_owned()), name);
887 }
888
889 if let Some(e) = &config.email {
890 log_error__set_field_for_default_user!(u, email, Some(e.to_owned()), name);
891 }
892 if let Some(f) = &config.first_name {
893 log_error__set_field_for_default_user!(u, first_name, Some(f.to_owned()), name);
894 }
895 if let Some(l) = &config.last_name {
896 log_error__set_field_for_default_user!(u, last_name, Some(l.to_owned()), name);
897 }
898 }
899 Err(e) => {
900 om::log_error!(
901 "{}: Failed to initialize default user '{}'",
902 *BASE_MSG,
903 name
904 );
905 log_error!("{}", e);
906 }
907 }
908 }
909
910 if let Some(func) = &crate::ORIGEN_CONFIG.user__current_user_lookup_function {
912 users.set_lookup_current_id_function(Some(
913 pyapi_metal::_helpers::get_qualified_attr(&func)?.as_ref(py),
914 ))?;
915 }
916
917 if ORIGEN_CONFIG
919 .initial_user
920 .as_ref()
921 .map_or(true, |u| u.initialize.unwrap_or(true))
922 {
923 match users.lookup_current_id(true) {
924 Ok(_) => {
925 if ORIGEN_CONFIG
926 .initial_user
927 .as_ref()
928 .map_or(true, |u| u.init_home_dir.unwrap_or(true))
929 {
930 match users.current_user() {
931 Ok(usr) => match usr {
932 Some(u) => match u.set_home_dir(None) {
933 Ok(_) => {}
934 Err(e) => {
935 log_error!(
936 "{}: Failed to lookup current user's home directory",
937 *BASE_MSG
938 );
939 log_error!("{}", e);
940 }
941 },
942 None => {
943 log_error!("{}: Failed to lookup current user", *BASE_MSG);
944 }
945 },
946 Err(e) => {
947 log_error!("{}: Failed to lookup current user", *BASE_MSG);
948 log_error!("{}", e);
949 }
950 }
951 }
952 }
953 Err(e) => {
954 log_error!("{}: Failed to lookup current user", *BASE_MSG);
955 log_error!("{}", e);
956 }
957 }
958 } else {
959 log_trace!("Bypassing current user initialization.");
960 }
961 Ok(users)
962}
963
964#[cfg(test)]
965mod tests {
966 use super::*;
967
968 #[test]
969 fn initializes_module_and_interoperates_with_pin_actions() -> PyResult<()> {
970 Python::with_gil(|py| {
971 let module = PyModule::new(py, "_origen")?;
972 _origen(py, module)?;
973
974 let pin_actions = module
975 .getattr("dut")?
976 .getattr("pins")?
977 .getattr("PinActions")?;
978 let created = pin_actions.call1(("1",))?;
979 let class_method = pin_actions.call_method0("DriveHigh")?;
980
981 assert!(created.eq(class_method)?);
982 let combined = pin_actions.call1((class_method,))?;
983 assert!(combined.eq(created)?);
984 Ok(())
985 })
986 }
987}