ixa/
external_api.rs

1// Now all features of the external API are used internally, so we expect dead code.
2#![allow(dead_code)]
3
4use serde::de::DeserializeOwned;
5use serde::{Deserialize, Serialize};
6
7use crate::context::Context;
8use crate::error::IxaError;
9
10pub(crate) trait ExtApi {
11    type Args: DeserializeOwned;
12    type Retval: Serialize;
13
14    fn run(context: &mut Context, args: &Self::Args) -> Result<Self::Retval, IxaError>;
15}
16
17#[derive(Serialize, Deserialize, Debug)]
18pub(crate) struct EmptyArgs {}
19
20pub(crate) fn run_ext_api<T: ExtApi>(
21    context: &mut Context,
22    args: &T::Args,
23) -> Result<T::Retval, IxaError> {
24    T::run(context, args)
25}
26
27pub(crate) mod global_properties {
28    use clap::{Parser, Subcommand};
29    use serde::{Deserialize, Serialize};
30
31    use crate::context::Context;
32    use crate::global_properties::ContextGlobalPropertiesExt;
33    use crate::IxaError;
34
35    pub(crate) struct Api {}
36    #[derive(Serialize, Deserialize, Debug)]
37    pub(crate) enum Retval {
38        List(Vec<String>),
39        Value(String),
40    }
41    #[derive(Subcommand, Clone, Debug, Serialize, Deserialize)]
42    /// Access global properties
43    pub(crate) enum ArgsEnum {
44        /// List all global properties
45        List,
46
47        /// Get the value of a global property
48        Get {
49            /// The property name
50            property: String,
51        },
52    }
53
54    #[derive(Parser, Debug, Serialize, Deserialize)]
55    pub(crate) enum Args {
56        #[command(subcommand)]
57        Global(ArgsEnum),
58    }
59    impl super::ExtApi for Api {
60        type Args = Args;
61        type Retval = Retval;
62
63        fn run(context: &mut Context, args: &Args) -> Result<Retval, IxaError> {
64            let Args::Global(global_args) = args;
65
66            match global_args {
67                ArgsEnum::List => Ok(Retval::List(context.list_registered_global_properties())),
68                ArgsEnum::Get { property: name } => {
69                    let output = context.get_serialized_value_by_string(name)?;
70                    match output {
71                        Some(value) => Ok(Retval::Value(value)),
72                        None => Err(IxaError::PropertyNotSet { name: name.clone() }),
73                    }
74                }
75            }
76        }
77    }
78}
79
80pub(crate) mod breakpoint {
81    use clap::{Parser, Subcommand};
82    use serde::{Deserialize, Serialize};
83
84    use crate::context::Context;
85    use crate::debugger::enter_debugger;
86    use crate::{trace, IxaError};
87
88    #[derive(Subcommand, Clone, Debug, Serialize, Deserialize)]
89    /// Manipulate Debugger Breakpoints
90    pub(crate) enum ArgsEnum {
91        /// List all scheduled breakpoints
92        List,
93        /// Set a breakpoint at a given time
94        Set {
95            #[arg(required = true)]
96            time: f64,
97            #[arg(long, hide = true, default_value_t = true)]
98            console: bool,
99        },
100        /// Delete the breakpoint with the specified id.
101        /// Providing the `--all` option removes all breakpoints.
102        #[group(multiple = false, required = true)]
103        Delete {
104            /// The ID of the breakpoint to delete
105            #[arg(value_name = "ID")]
106            id: Option<u32>,
107
108            /// Remove all breakpoints
109            #[arg(long, action)]
110            all: bool,
111        },
112        /// Disables but does not delete breakpoints globally
113        Disable,
114        /// Enables breakpoints globally
115        Enable,
116    }
117
118    #[derive(Parser, Debug, Serialize, Deserialize)]
119    pub(crate) enum Args {
120        #[command(subcommand)]
121        Breakpoint(ArgsEnum),
122    }
123
124    #[derive(Serialize, Deserialize, Debug)]
125    pub(crate) enum Retval {
126        List(Vec<String>),
127        Ok,
128    }
129
130    pub(crate) struct Api {}
131    impl super::ExtApi for Api {
132        type Args = Args;
133        type Retval = Retval;
134
135        fn run(context: &mut Context, args: &Args) -> Result<Retval, IxaError> {
136            let Args::Breakpoint(breakpoint_args) = args;
137
138            match breakpoint_args {
139                ArgsEnum::List => {
140                    trace!("Listing breakpoints");
141                    let list = context.list_breakpoints(0);
142                    let list = list
143                        .iter()
144                        .map(|schedule| {
145                            format!(
146                                "{}: t={} ({})",
147                                schedule.plan_id, schedule.time, schedule.priority
148                            )
149                        })
150                        .collect::<Vec<String>>();
151                    Ok(Retval::List(list))
152                }
153
154                #[allow(unused_variables)]
155                ArgsEnum::Set { time, console } => {
156                    if *time < context.get_current_time() {
157                        return Err(IxaError::BreakpointTimeInPast { time: *time });
158                    }
159                    context.schedule_debugger(*time, None, Box::new(enter_debugger));
160
161                    trace!("Breakpoint set at t={time}");
162                    Ok(Retval::Ok)
163                }
164
165                ArgsEnum::Delete { id, all } => {
166                    if let Some(id) = id {
167                        assert!(!all);
168                        trace!("Deleting breakpoint {id}");
169                        let cancelled = context.delete_breakpoint(u64::from(*id));
170                        if cancelled.is_none() {
171                            Err(IxaError::BreakpointNotFound { id: *id })
172                        } else {
173                            Ok(Retval::Ok)
174                        }
175                    } else {
176                        assert!(all);
177                        trace!("Deleting all breakpoints");
178                        context.clear_breakpoints();
179                        Ok(Retval::Ok)
180                    }
181                }
182
183                ArgsEnum::Disable => {
184                    trace!("Disabling all breakpoints");
185                    context.disable_breakpoints();
186                    Ok(Retval::Ok)
187                }
188
189                ArgsEnum::Enable => {
190                    trace!("Enabling all breakpoints");
191                    context.enable_breakpoints();
192                    Ok(Retval::Ok)
193                }
194            }
195        }
196    }
197}
198
199pub(crate) mod next {
200    use clap::Parser;
201    use serde::Serialize;
202    use serde_derive::Deserialize;
203
204    use crate::context::Context;
205    use crate::external_api::EmptyArgs;
206    use crate::IxaError;
207
208    #[derive(Parser, Debug, Serialize, Deserialize)]
209    pub enum Args {
210        /// Execute the next item in the event loop
211        Next,
212    }
213
214    #[derive(Serialize)]
215    pub(crate) enum Retval {
216        Ok,
217    }
218    #[allow(unused)]
219    pub(crate) struct Api {}
220    impl super::ExtApi for Api {
221        type Args = EmptyArgs;
222        type Retval = Retval;
223
224        fn run(_context: &mut Context, _args: &EmptyArgs) -> Result<Retval, IxaError> {
225            // This is a no-op which allows for arg checking.
226            Ok(Retval::Ok)
227        }
228    }
229}
230
231pub(crate) mod halt {
232    use clap::Parser;
233    use serde::Serialize;
234    use serde_derive::Deserialize;
235
236    use crate::context::Context;
237    use crate::external_api::EmptyArgs;
238    use crate::IxaError;
239
240    #[derive(Parser, Debug, Serialize, Deserialize)]
241    pub enum Args {
242        /// End the simulation
243        Halt,
244    }
245
246    #[derive(Serialize)]
247    pub(crate) enum Retval {
248        Ok,
249    }
250    #[allow(unused)]
251    pub(crate) struct Api {}
252    impl super::ExtApi for Api {
253        type Args = EmptyArgs;
254        type Retval = Retval;
255
256        fn run(_context: &mut Context, _args: &EmptyArgs) -> Result<Retval, IxaError> {
257            // This is a no-op which allows for arg checking.
258            Ok(Retval::Ok)
259        }
260    }
261}
262
263pub(crate) mod r#continue {
264    use clap::Parser;
265    use serde_derive::{Deserialize, Serialize};
266
267    use crate::context::Context;
268    use crate::external_api::EmptyArgs;
269    use crate::IxaError;
270
271    #[derive(Parser, Debug, Serialize, Deserialize)]
272    pub enum Args {
273        /// Continue running the simulation
274        Continue,
275    }
276
277    #[derive(Serialize)]
278    pub(crate) enum Retval {
279        Ok,
280    }
281    #[allow(unused)]
282    pub(crate) struct Api {}
283    impl super::ExtApi for Api {
284        type Args = EmptyArgs;
285        type Retval = Retval;
286
287        fn run(_context: &mut Context, _args: &EmptyArgs) -> Result<Retval, IxaError> {
288            // This is a no-op which allows for arg checking.
289            Ok(Retval::Ok)
290        }
291    }
292}
293
294pub(crate) mod time {
295    #![allow(clippy::float_cmp)]
296    use clap::Parser;
297    use serde::{Deserialize, Serialize};
298
299    use crate::context::Context;
300    use crate::external_api::EmptyArgs;
301    use crate::IxaError;
302
303    pub(crate) struct Api {}
304    #[derive(Parser, Debug, Deserialize)]
305    pub(crate) enum Args {
306        /// Get the time of the simulation.
307        Time,
308    }
309
310    #[derive(Serialize)]
311    pub(crate) struct Retval {
312        pub time: f64,
313    }
314    impl super::ExtApi for Api {
315        type Args = super::EmptyArgs;
316        type Retval = Retval;
317
318        fn run(context: &mut Context, _args: &EmptyArgs) -> Result<Retval, IxaError> {
319            Ok(Retval {
320                time: context.get_current_time(),
321            })
322        }
323    }
324
325    #[cfg(test)]
326    mod test {
327        use crate::Context;
328
329        #[test]
330        fn test() {
331            let mut context = Context::new();
332
333            let result = crate::external_api::run_ext_api::<super::Api>(
334                &mut context,
335                &crate::external_api::EmptyArgs {},
336            );
337
338            assert_eq!(result.unwrap().time, 0.0);
339        }
340    }
341}