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 Ok(())
614 });
615 Ok(Some(ret.into()))
616 } else {
617 Ok(None)
618 }
619}
620
621#[pyfunction]
624fn clean_mode(name: &str) -> PyResult<String> {
625 let c = origen::clean_mode(name);
626 Ok(c)
627}
628
629#[pyfunction]
630fn target_file(name: &str, dir: &str) -> PyResult<String> {
634 let c = clean_target!(name, dir, true);
635 Ok(c)
636}
637
638#[pyfunction]
639fn output_directory(py: Python) -> PyResult<PyObject> {
640 let dir = origen::STATUS.output_dir();
641 Ok(pypath!(py, dir.display()))
642}
643
644#[pyfunction]
645fn website_output_directory(py: Python) -> PyResult<PyObject> {
646 let dir = origen::app().unwrap().website_output_directory();
647 Ok(pypath!(py, dir.display()))
648}
649
650#[pyfunction]
651fn website_source_directory(py: Python) -> PyResult<PyObject> {
652 let dir = origen::app().unwrap().website_source_directory();
653 Ok(pypath!(py, dir.display()))
654}
655
656#[pyfunction]
657fn prepare_for_target_load() -> PyResult<()> {
659 origen::prepare_for_target_load()?;
660 Ok(())
661}
662
663#[pyfunction]
664fn start_new_test(name: Option<String>) -> PyResult<()> {
666 origen::start_new_test(name);
667 Ok(())
668}
669
670pub fn pickle(py: Python, object: &PyAny) -> PyResult<Vec<u8>> {
671 let pickle = PyModule::import(py, "pickle")?;
672 pickle
673 .getattr("dumps")?
674 .call1((object,))?
675 .extract::<Vec<u8>>()
676}
677
678pub fn depickle<'a>(py: Python<'a>, object: &Vec<u8>) -> PyResult<&'a PyAny> {
679 let pickle = PyModule::import(py, "pickle")?;
680 let bytes = PyBytes::new(py, object);
681 pickle.getattr("loads")?.call1((bytes,))
682}
683
684pub fn with_pycallbacks<T, F>(mut func: F) -> PyResult<T>
685where
686 F: FnMut(Python, &PyAny) -> PyResult<T>,
687{
688 Python::with_gil(|py| {
689 let pycallbacks = py.import("origen.callbacks")?;
690 func(py, pycallbacks)
691 })
692}
693
694pub fn get_full_class_name(obj: &PyAny) -> PyResult<String> {
695 let cls = obj.getattr("__class__")?;
696 let mut n = cls.getattr("__module__")?.extract::<String>()?;
697 n.push_str(&format!(
698 ".{}",
699 cls.getattr("__qualname__")?.extract::<String>()?
700 ));
701 Ok(n)
702}
703
704#[pyfunction]
706pub fn boot_users(py: Python) -> PyResult<pyapi_metal::framework::users::Users> {
707 lazy_static! {
708 static ref BASE_MSG: &'static str = "Encountered an error when initializing users";
709 }
710
711 log_trace!("Initializing Users...");
712 if let Some(r) = &ORIGEN_CONFIG.session__user_root {
713 log_trace!("Setting user session root to {}", r);
714 let mut users = om::users_mut();
715 let sc = users.default_session_config_mut();
716 sc.root = Some(PathBuf::from(r));
717 }
718
719 let users = pyapi_metal::framework::users::users()?;
720
721 if let Some(pw_cache_option) = &crate::ORIGEN_CONFIG.user__password_cache_option {
722 match users.set_default_password_cache_option(Some(pw_cache_option)) {
723 Ok(_) => {}
724 Err(e) => {
725 om::log_error!(
726 "{}: Error encountered updating default password cache option",
727 *BASE_MSG
728 );
729 om::log_error!("{}", e);
730 }
731 }
732 }
733
734 if let Some(dsets) = &crate::ORIGEN_CONFIG.user__datasets {
735 let mut replace_default = true;
736 for (dn, config) in dsets {
737 log_trace!("Adding user dataset {}", dn);
738 match config.try_into() {
739 Ok(om_config) => {
740 match pyapi_metal::framework::users::UserDatasetConfig::new_py(py, om_config) {
741 Ok(py_config) => {
742 if replace_default {
743 match users.override_default_dataset(
744 dn,
745 Some(py_config.into_py(py).as_ref(py)),
746 ) {
747 Ok(_) => {
748 replace_default = false;
749 }
750 Err(e) => {
751 om::log_error!("{}: Error encountered updating default dataset with config '{}'", *BASE_MSG, dn);
752 om::log_error!("{}", e);
753 }
754 }
755 } else {
756 match users.add_dataset(
757 dn,
758 Some(
759 pyapi_metal::framework::users::UserDatasetConfig::new_py(
760 py,
761 config.try_into()?,
762 )?
763 .into_py(py)
764 .as_ref(py),
765 ),
766 false,
767 ) {
768 Ok(_) => {}
769 Err(e) => {
770 om::log_error!(
771 "{}: Error encountered adding dataset '{}'",
772 *BASE_MSG,
773 dn
774 );
775 om::log_error!("{}", e);
776 }
777 }
778 }
779 }
780 Err(e) => {
781 om::log_error!(
783 "{}: Error encountered processing dataset config for '{}'",
784 *BASE_MSG,
785 dn
786 );
787 om::log_error!("{}", e);
788 }
789 }
790 }
791 Err(e) => {
792 om::log_error!(
793 "{}: Error encountered processing dataset config for '{}'",
794 *BASE_MSG,
795 dn
796 );
797 om::log_error!("{}", e);
798 }
799 }
800 }
801 }
802
803 if let Some(hierarchy) = &crate::ORIGEN_CONFIG.user__data_lookup_hierarchy {
805 match users.apply_data_lookup_hierarchy(hierarchy.to_owned()) {
806 Ok(_) => {}
807 Err(e) => {
808 om::log_error!(
809 "{}: Error encountered setting the default lookup hierarchy",
810 *BASE_MSG
811 );
812 om::log_error!("{}", e);
813 om::log_error!("Forcing empty dataset lookup hierarchy...");
814 users.apply_data_lookup_hierarchy(vec![])?;
815 }
816 }
817 } else {
818 if crate::ORIGEN_CONFIG.user__datasets.is_some()
819 && crate::ORIGEN_CONFIG.user__datasets.as_ref().unwrap().len() > 1
820 {
821 users.apply_data_lookup_hierarchy(vec![])?;
824 }
825 }
826
827 for (m, ds) in &crate::ORIGEN_CONFIG.user__dataset_motives {
829 match users.add_motive(m, ds, false) {
830 Ok(_) => {}
831 Err(e) => {
832 om::log_error!(
833 "{}: Error encountered adding dataset motive '{}'",
834 *BASE_MSG,
835 m
836 );
837 om::log_error!("{}", e);
838 }
839 }
840 }
841
842 macro_rules! log_error__set_field_for_default_user {
843 ($u:expr, $field:tt, $val:expr, $name:expr ) => {
844 paste! {
845 match $u.[<set_ $field>]($val) {
846 Ok(_) => {},
847 Err(e) => {
848 log_error!("{}: Failed to initialize default user '{}'", *BASE_MSG, $name);
849 log_error!(" Failed to set field '{}'", stringify!($field));
850 log_error!("{}", e);
851 log_error!("Bailing on initializing default user '{}'", $name);
852 continue;
853 }
854 }
855 }
856 };
857 }
858
859 for (name, config) in &crate::ORIGEN_CONFIG.default_users {
861 match users.add(name, config.auto_populate) {
862 Ok(u) => {
863 if let Some(s) = config.should_validate_passwords {
864 log_error__set_field_for_default_user!(
865 u,
866 should_validate_passwords,
867 Some(s),
868 name
869 )
870 }
871 if let Some(uname) = &config.username {
872 log_error__set_field_for_default_user!(
873 u,
874 username,
875 Some(uname.to_owned()),
876 name
877 );
878 }
879 if let Some(pw) = &config.password {
880 log_error__set_field_for_default_user!(u, password, Some(pw.to_owned()), name);
881 }
882
883 if let Some(e) = &config.email {
884 log_error__set_field_for_default_user!(u, email, Some(e.to_owned()), name);
885 }
886 if let Some(f) = &config.first_name {
887 log_error__set_field_for_default_user!(u, first_name, Some(f.to_owned()), name);
888 }
889 if let Some(l) = &config.last_name {
890 log_error__set_field_for_default_user!(u, last_name, Some(l.to_owned()), name);
891 }
892 }
893 Err(e) => {
894 om::log_error!(
895 "{}: Failed to initialize default user '{}'",
896 *BASE_MSG,
897 name
898 );
899 log_error!("{}", e);
900 }
901 }
902 }
903
904 if let Some(func) = &crate::ORIGEN_CONFIG.user__current_user_lookup_function {
906 users.set_lookup_current_id_function(Some(
907 pyapi_metal::_helpers::get_qualified_attr(&func)?.as_ref(py),
908 ))?;
909 }
910
911 if ORIGEN_CONFIG
913 .initial_user
914 .as_ref()
915 .map_or(true, |u| u.initialize.unwrap_or(true))
916 {
917 match users.lookup_current_id(true) {
918 Ok(_) => {
919 if ORIGEN_CONFIG
920 .initial_user
921 .as_ref()
922 .map_or(true, |u| u.init_home_dir.unwrap_or(true))
923 {
924 match users.current_user() {
925 Ok(usr) => match usr {
926 Some(u) => match u.set_home_dir(None) {
927 Ok(_) => {}
928 Err(e) => {
929 log_error!(
930 "{}: Failed to lookup current user's home directory",
931 *BASE_MSG
932 );
933 log_error!("{}", e);
934 }
935 },
936 None => {
937 log_error!("{}: Failed to lookup current user", *BASE_MSG);
938 }
939 },
940 Err(e) => {
941 log_error!("{}: Failed to lookup current user", *BASE_MSG);
942 log_error!("{}", e);
943 }
944 }
945 }
946 }
947 Err(e) => {
948 log_error!("{}: Failed to lookup current user", *BASE_MSG);
949 log_error!("{}", e);
950 }
951 }
952 } else {
953 log_trace!("Bypassing current user initialization.");
954 }
955 Ok(users)
956}
957
958#[cfg(test)]
959mod tests {
960 use super::*;
961
962 #[test]
963 fn initializes_module_and_interoperates_with_pin_actions() -> PyResult<()> {
964 Python::with_gil(|py| {
965 let module = PyModule::new(py, "_origen")?;
966 _origen(py, module)?;
967
968 let pin_actions = module
969 .getattr("dut")?
970 .getattr("pins")?
971 .getattr("PinActions")?;
972 let created = pin_actions.call1(("1",))?;
973 let class_method = pin_actions.call_method0("DriveHigh")?;
974
975 assert!(created.eq(class_method)?);
976 let combined = pin_actions.call1((class_method,))?;
977 assert!(combined.eq(created)?);
978 Ok(())
979 })
980 }
981}