powerio_capi/
diagnostics.rs1pub use powerio_core::{
11 Diagnostic, DiagnosticInfo, DiagnosticSeverity, check_registry, check_scope_ownership,
12};
13
14pub mod codes {
15 powerio_core::diagnostic_codes! {
16 BIND_CAPI_NULL_HANDLE = "BIND.CAPI.NULL_HANDLE", Error,
17 "a handle argument was NULL", category = Data;
18 BIND_CAPI_NULL_ARGUMENT = "BIND.CAPI.NULL_ARGUMENT", Error,
19 "a required non-handle argument was NULL", category = Data;
20 BIND_CAPI_INVALID_UTF8 = "BIND.CAPI.INVALID_UTF8", Error,
21 "a string argument is not valid UTF-8", category = Data;
22 BIND_CAPI_INDEX_OUT_OF_RANGE = "BIND.CAPI.INDEX_OUT_OF_RANGE", Error,
23 "an index argument cannot be converted or is out of range", category = Data;
24 BIND_CAPI_PANIC = "BIND.CAPI.PANIC", Error,
25 "a panic was caught at the boundary and did not cross it", category = Data;
26 BIND_CAPI_INVALID_OPTIONS = "BIND.CAPI.INVALID_OPTIONS", Error,
27 "an options struct declared a size or a field value this build cannot honor",
28 category = Data;
29 EMIT_CAPI_SERIALIZE_FAILED = "EMIT.CAPI.SERIALIZE_FAILED", Error,
30 "a document or table an entry point returns could not be built or serialized",
31 category = Output;
32 BIND_CAPI_UNCODED_FAILURE = "BIND.CAPI.UNCODED_FAILURE", Error,
33 "a library failure reached the boundary carrying no finding of its own",
34 category = Data;
35 REQUEST_CAPI_UNKNOWN_FORMULA = "REQUEST.CAPI.UNKNOWN_FORMULA", Error,
36 "the caller named a branch susceptance formula this surface does not have",
37 category = Request;
38 REQUEST_CAPI_TYPE_MISMATCH = "REQUEST.CAPI.TYPE_MISMATCH", Error,
39 "the value does not have the structural type required by the operation",
40 category = Request;
41 REQUEST_CAPI_QUANTITY_UNKNOWN = "REQUEST.CAPI.QUANTITY_UNKNOWN", Error,
42 "the requested operating point quantity is not defined",
43 category = Request;
44 REQUEST_CAPI_ALLOCATION_UNKNOWN = "REQUEST.CAPI.ALLOCATION_UNKNOWN", Error,
45 "the requested load allocation rule is not defined",
46 category = Request;
47 }
48}
49
50#[must_use]
52pub fn registry() -> Vec<&'static DiagnosticInfo> {
53 codes::ALL.to_vec()
54}
55
56#[cfg(all(test, feature = "dist", feature = "prob", feature = "matrix"))]
60mod workspace {
61 use super::*;
62
63 fn registries() -> Vec<(&'static str, Vec<&'static DiagnosticInfo>)> {
64 vec![
65 ("powerio-tx", powerio_tx::diagnostics::registry()),
66 ("powerio (stored + transform)", powerio::codes::registry()),
67 #[cfg(feature = "gridfm")]
68 (
69 "powerio (gridfm reader)",
70 powerio::gridfm_codes::ALL.to_vec(),
71 ),
72 ("powerio-dist", powerio_dist::diagnostics::registry()),
73 ("powerio-matrix", powerio_matrix::diagnostics::registry()),
74 ("powerio-prob", powerio_prob::diagnostics::registry()),
75 ("powerio-capi", registry()),
76 ]
77 }
78
79 #[test]
80 fn every_code_in_the_workspace_is_registered_once_and_well_formed() {
81 let all: Vec<&DiagnosticInfo> = registries()
82 .into_iter()
83 .flat_map(|(_, entries)| entries)
84 .collect();
85 let problems = check_registry(all.iter().copied());
86 assert!(problems.is_empty(), "{problems:#?}");
87 }
88
89 #[test]
90 fn no_two_crates_claim_one_scope() {
91 let owned = registries();
92 let borrowed: Vec<(&str, &[&DiagnosticInfo])> = owned
93 .iter()
94 .map(|(name, entries)| (*name, entries.as_slice()))
95 .collect();
96 let problems = check_scope_ownership(&borrowed);
97 assert!(problems.is_empty(), "{problems:#?}");
98 }
99
100 #[test]
106 fn every_code_string_the_abi_emits_is_registered() {
107 let source = include_str!("lib.rs")
108 .split("#[cfg(test)]")
109 .next()
110 .expect("split yields the leading source");
111 let registered: std::collections::BTreeSet<&str> = registries()
112 .into_iter()
113 .flat_map(|(_, entries)| entries)
114 .map(|entry| entry.code)
115 .collect();
116 for piece in source.split('"').skip(1).step_by(2) {
117 let dotted = piece.split('.').count() >= 3
118 && piece.split('.').all(|segment| {
119 !segment.is_empty()
120 && segment.bytes().all(|byte| {
121 byte.is_ascii_uppercase() || byte.is_ascii_digit() || byte == b'_'
122 })
123 });
124 if dotted {
125 assert!(registered.contains(piece), "`{piece}` is not registered");
126 }
127 }
128 }
129}