wowlab_wasm/
sim_config.rs1use std::{error::Error, fmt};
2
3use wasm_bindgen::prelude::*;
4use wowlab_common::sim::{
5 intent::IntentConfigError, job_meta::JobMetaError, sentinel_config::SentinelConfigError,
6};
7use wowlab_types::{
8 game::ImplementedSpecInfo,
9 wasm::{WasmCommonError, from_js},
10};
11
12#[derive(Debug)]
14#[non_exhaustive]
15pub struct WasmSimConfigError {
16 kind: WasmSimConfigErrorKind,
17}
18
19#[derive(Debug)]
20enum WasmSimConfigErrorKind {
21 Input(WasmCommonError),
22 Intent(IntentConfigError),
23 Sentinel(SentinelConfigError),
24 JobMeta(JobMetaError),
25}
26
27impl fmt::Display for WasmSimConfigError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match &self.kind {
30 WasmSimConfigErrorKind::Input(error) => fmt::Display::fmt(error, f),
31 WasmSimConfigErrorKind::Intent(error) => fmt::Display::fmt(error, f),
32 WasmSimConfigErrorKind::Sentinel(error) => fmt::Display::fmt(error, f),
33 WasmSimConfigErrorKind::JobMeta(error) => {
34 write!(f, "parse job metadata: {error}")
35 }
36 }
37 }
38}
39
40impl Error for WasmSimConfigError {
41 fn source(&self) -> Option<&(dyn Error + 'static)> {
42 match &self.kind {
43 WasmSimConfigErrorKind::Input(_) => None,
44 WasmSimConfigErrorKind::Intent(error) => Some(error),
45 WasmSimConfigErrorKind::Sentinel(error) => Some(error),
46 WasmSimConfigErrorKind::JobMeta(error) => Some(error),
47 }
48 }
49}
50
51impl From<WasmCommonError> for WasmSimConfigError {
52 fn from(error: WasmCommonError) -> Self {
53 Self {
54 kind: WasmSimConfigErrorKind::Input(error),
55 }
56 }
57}
58
59impl From<IntentConfigError> for WasmSimConfigError {
60 fn from(error: IntentConfigError) -> Self {
61 Self {
62 kind: WasmSimConfigErrorKind::Intent(error),
63 }
64 }
65}
66
67impl From<SentinelConfigError> for WasmSimConfigError {
68 fn from(error: SentinelConfigError) -> Self {
69 Self {
70 kind: WasmSimConfigErrorKind::Sentinel(error),
71 }
72 }
73}
74
75impl From<JobMetaError> for WasmSimConfigError {
76 fn from(error: JobMetaError) -> Self {
77 Self {
78 kind: WasmSimConfigErrorKind::JobMeta(error),
79 }
80 }
81}
82
83impl From<WasmSimConfigError> for JsValue {
84 fn from(error: WasmSimConfigError) -> Self {
85 let name = match &error.kind {
86 WasmSimConfigErrorKind::Input(_) => "DecodeError",
87 WasmSimConfigErrorKind::Intent(_) | WasmSimConfigErrorKind::Sentinel(_) => {
88 "ConfigError"
89 }
90 WasmSimConfigErrorKind::JobMeta(_) => "ParseError",
91 };
92 let js_error = js_sys::Error::new(&error.to_string());
93
94 js_error.set_name(name);
95
96 js_error.into()
97 }
98}
99
100#[wasm_bindgen(js_name = buildSimConfig)]
102pub fn wasm_build_sim_config(input: JsValue) -> Result<String, WasmSimConfigError> {
103 let input: wowlab_common::sim::intent::IntentInput = from_js(input)?;
104
105 Ok(wowlab_common::sim::intent::build_sim_config(input)?)
106}
107
108#[wasm_bindgen(js_name = buildSentinelConfig)]
110pub fn wasm_build_sentinel_config(input: JsValue) -> Result<String, WasmSimConfigError> {
111 let config: wowlab_common::sim::sentinel_config::SentinelConfig = from_js(input)?;
112
113 Ok(wowlab_common::sim::sentinel_config::build_sentinel_config(
114 &config,
115 )?)
116}
117
118#[wasm_bindgen(js_name = parseImplementedSpecs)]
120pub fn wasm_parse_implemented_specs(
121 input: JsValue,
122) -> Result<Vec<ImplementedSpecInfo>, WasmCommonError> {
123 from_js(input)
124}
125
126#[wasm_bindgen(js_name = parseJobMeta)]
127pub fn wasm_parse_job_meta(
128 json: &str,
129) -> Result<wowlab_common::sim::job_meta::JobMeta, WasmSimConfigError> {
130 Ok(wowlab_common::sim::job_meta::parse_job_meta(json)?)
131}
132
133#[cfg(test)]
134mod tests {
135 use std::error::Error as _;
136
137 use googletest::prelude::*;
138
139 use super::*;
140
141 #[gtest]
142 fn intent_error_retains_common_and_toml_sources_until_javascript_conversion() -> Result<()> {
143 let common_error = wowlab_common::sim::intent::parse_sim_config("[")
144 .err()
145 .or_fail()?;
146 let error = WasmSimConfigError::from(common_error);
147
148 let common_source = error.source().or_fail()?;
149
150 verify_true!(common_source.is::<IntentConfigError>())?;
151
152 verify_that!(common_source.source(), some(anything()))
153 }
154
155 #[gtest]
156 fn sentinel_error_retains_common_and_toml_sources_until_javascript_conversion() -> Result<()> {
157 let common_error = wowlab_common::sim::sentinel_config::parse_sentinel_config("[")
158 .err()
159 .or_fail()?;
160 let error = WasmSimConfigError::from(common_error);
161
162 let common_source = error.source().or_fail()?;
163
164 verify_true!(common_source.is::<SentinelConfigError>())?;
165
166 verify_that!(common_source.source(), some(anything()))
167 }
168
169 #[gtest]
170 fn job_meta_error_retains_common_and_json_sources_until_javascript_conversion() -> Result<()> {
171 let error = wasm_parse_job_meta("{").err().or_fail()?;
172
173 let common_source = error.source().or_fail()?;
174
175 verify_true!(common_source.is::<JobMetaError>())?;
176 verify_that!(common_source.source(), some(anything()))?;
177
178 verify_that!(
179 error.to_string(),
180 starts_with("parse job metadata: JSON parse error:")
181 )
182 }
183}