ledger_sim/drivers/
mod.rs

1//! Drivers for speculos runtime execution
2
3use core::fmt::Debug;
4
5use async_trait::async_trait;
6use strum::{Display, EnumString, EnumVariantNames};
7
8use crate::Options;
9
10mod local;
11pub use local::{LocalDriver, LocalHandle};
12
13mod docker;
14pub use docker::{DockerDriver, DockerHandle};
15
16/// Mode selector for generic drivers
17#[derive(Copy, Clone, PartialEq, Debug, clap::ValueEnum, EnumString, EnumVariantNames, Display)]
18#[strum(serialize_all = "lowercase")]
19pub enum DriverMode {
20    /// Run Speculos as child process (requires that `speculos.py` is available on the PATH)
21    Local,
22    /// Run Speculos via docker container
23    Docker,
24}
25
26/// [`Driver`] trait for speculos providers
27#[async_trait]
28pub trait Driver {
29    type Handle: Debug;
30
31    /// Run speculos with the specified app and options
32    async fn run(&self, app: &str, opts: Options) -> anyhow::Result<Self::Handle>;
33
34    /// Wait for task exit / completion
35    async fn wait(&self, handle: &mut Self::Handle) -> anyhow::Result<()>;
36
37    /// Exit task
38    async fn exit(&self, mut handle: Self::Handle) -> anyhow::Result<()>;
39}
40
41/// Generic driver helper, allows implementations to be abstract over
42/// concrete driver types
43pub enum GenericDriver {
44    Local(LocalDriver),
45    Docker(DockerDriver),
46}
47
48impl GenericDriver {
49    /// Create a new [GenericDriver] with the specified [DriverMode]
50    pub fn new(mode: DriverMode) -> Result<Self, anyhow::Error> {
51        let d = match mode {
52            DriverMode::Local => Self::Local(LocalDriver::new()),
53            DriverMode::Docker => Self::Docker(DockerDriver::new()?),
54        };
55        Ok(d)
56    }
57}
58
59/// Generic Handle helper for use with [GenericDriver]
60#[derive(Debug)]
61pub enum GenericHandle {
62    Local(LocalHandle),
63    Docker(DockerHandle),
64}
65
66/// [Driver] implementation for [GenericDriver], calls out to [LocalDriver] or
67/// [DockerDriver] depending on configuration.
68#[async_trait]
69impl Driver for GenericDriver {
70    type Handle = GenericHandle;
71
72    async fn run(&self, app: &str, opts: Options) -> anyhow::Result<Self::Handle> {
73        let h = match self {
74            GenericDriver::Local(d) => d.run(app, opts).await.map(GenericHandle::Local)?,
75            GenericDriver::Docker(d) => d.run(app, opts).await.map(GenericHandle::Docker)?,
76        };
77
78        Ok(h)
79    }
80
81    async fn wait(&self, handle: &mut Self::Handle) -> anyhow::Result<()> {
82        match (self, handle) {
83            (GenericDriver::Local(d), GenericHandle::Local(h)) => d.wait(h).await?,
84            (GenericDriver::Docker(d), GenericHandle::Docker(h)) => d.wait(h).await?,
85            _ => panic!("driver/handler mismatch"),
86        };
87        Ok(())
88    }
89
90    async fn exit(&self, handle: Self::Handle) -> anyhow::Result<()> {
91        match (self, handle) {
92            (GenericDriver::Local(d), GenericHandle::Local(h)) => d.exit(h).await?,
93            (GenericDriver::Docker(d), GenericHandle::Docker(h)) => d.exit(h).await?,
94            _ => panic!("driver/handler mismatch"),
95        };
96        Ok(())
97    }
98}