odra_cli/cmd/
deploy.rs

1use crate::{
2    cmd::DEPLOY_SUBCOMMAND, container::ContractError, custom_types::CustomTypeSet,
3    DeployedContractsContainer
4};
5use anyhow::Result;
6use clap::{ArgMatches, Command};
7use odra::{host::HostEnv, prelude::OdraError};
8use thiserror::Error;
9
10use super::MutableCommand;
11
12/// DeployCmd is a struct that represents the deploy command in the Odra CLI.
13///
14/// The deploy command runs the [DeployScript].
15pub(crate) struct DeployCmd {
16    pub script: Box<dyn DeployScript>
17}
18
19impl DeployCmd {
20    /// Creates a new instance of `DeployCmd` with the provided script.
21    pub fn new(script: impl DeployScript + 'static) -> Self {
22        DeployCmd {
23            script: Box::new(script)
24        }
25    }
26}
27
28impl MutableCommand for DeployCmd {
29    fn run(
30        &self,
31        env: &HostEnv,
32        _args: &ArgMatches,
33        _types: &CustomTypeSet,
34        container: &mut DeployedContractsContainer
35    ) -> Result<()> {
36        self.script.deploy(env, container)?;
37        Ok(())
38    }
39}
40
41impl From<&DeployCmd> for Command {
42    fn from(_value: &DeployCmd) -> Self {
43        Command::new(DEPLOY_SUBCOMMAND).about("Runs the deploy script")
44    }
45}
46
47/// Script that deploys contracts to the blockchain and stores contract data for further use.
48///
49/// In a deploy script, you can define the contracts that you want to deploy to the blockchain
50/// and write metadata to the container.
51pub trait DeployScript {
52    fn deploy(
53        &self,
54        env: &HostEnv,
55        container: &mut DeployedContractsContainer
56    ) -> core::result::Result<(), DeployError>;
57}
58
59/// Error that occurs during contract deployment.
60#[derive(Debug, Error)]
61pub enum DeployError {
62    #[error("Deploy error: {message}")]
63    OdraError { message: String },
64    #[error("Contract read error: {0}")]
65    ContractReadError(#[from] ContractError)
66}
67
68impl From<OdraError> for DeployError {
69    fn from(err: OdraError) -> Self {
70        DeployError::OdraError {
71            message: format!("{:?}", err)
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use crate::test_utils;
79
80    use super::*;
81    use odra::host::HostEnv;
82
83    struct MockDeployScript;
84
85    impl DeployScript for MockDeployScript {
86        fn deploy(
87            &self,
88            _env: &HostEnv,
89            _container: &mut DeployedContractsContainer
90        ) -> core::result::Result<(), DeployError> {
91            Ok(())
92        }
93    }
94
95    #[test]
96    fn deploy_cmd_run() {
97        // This is a placeholder test to ensure the DeployCmd can be instantiated and run.
98        let env = test_utils::mock_host_env();
99        let cmd = DeployCmd::new(MockDeployScript);
100        let mut container = test_utils::mock_contracts_container();
101        let result = cmd.run(
102            &env,
103            &ArgMatches::default(),
104            &CustomTypeSet::default(),
105            &mut container
106        );
107        assert!(result.is_ok());
108    }
109
110    #[test]
111    fn parsing_deploy_cmd() {
112        // This is a placeholder test to ensure the DeployCmd can be converted to a Command.
113        let cmd = DeployCmd::new(MockDeployScript);
114        let command: Command = (&cmd).into();
115        assert_eq!(command.get_name(), DEPLOY_SUBCOMMAND);
116        assert!(command.get_about().is_some());
117    }
118
119    #[test]
120    fn deploy_does_not_accept_args() {
121        // This is a placeholder test to ensure the DeployCmd can be converted to a Command.
122        let cmd = DeployCmd::new(MockDeployScript);
123        let command: Command = (&cmd).into();
124
125        let result = command.try_get_matches_from(vec!["test"]);
126        assert!(result.is_ok());
127
128        let command: Command = (&cmd).into();
129        assert_eq!(command.get_arguments().count(), 0);
130    }
131}