1use crate::{
2 cmd::{
3 args::{read_arg, Arg},
4 DEPLOY_SUBCOMMAND
5 },
6 container::ContractError,
7 custom_types::CustomTypeSet,
8 DeployedContractsContainer
9};
10use anyhow::Result;
11use clap::{ArgMatches, Command};
12use odra::{host::HostEnv, prelude::OdraError};
13use thiserror::Error;
14
15use super::MutableCommand;
16
17pub(crate) struct DeployCmd {
21 pub script: Box<dyn DeployScript>
22}
23
24impl DeployCmd {
25 pub fn new(script: impl DeployScript + 'static) -> Self {
27 DeployCmd {
28 script: Box::new(script)
29 }
30 }
31}
32
33impl MutableCommand for DeployCmd {
34 fn run(
35 &self,
36 env: &HostEnv,
37 args: &ArgMatches,
38 _types: &CustomTypeSet,
39 container: &mut DeployedContractsContainer
40 ) -> Result<()> {
41 let deploy_mode =
42 read_arg::<String>(args, Arg::DeployMode).ok_or(DeployError::MissingDeployMode)?;
43 crate::log(format!("Deploy mode: {}", deploy_mode));
44 container.apply_deploy_mode(deploy_mode)?;
45
46 self.script.deploy(env, container)?;
47 Ok(())
48 }
49}
50
51impl From<&DeployCmd> for Command {
52 fn from(_value: &DeployCmd) -> Self {
53 Command::new(DEPLOY_SUBCOMMAND)
54 .arg(Arg::DeployMode)
55 .about("Runs the deploy script")
56 }
57}
58
59pub trait DeployScript {
64 fn deploy(
65 &self,
66 env: &HostEnv,
67 container: &mut DeployedContractsContainer
68 ) -> core::result::Result<(), DeployError>;
69}
70
71#[derive(Debug, Error)]
73pub enum DeployError {
74 #[error("Deploy error: {message}")]
75 OdraError { message: String },
76 #[error("Contract read error: {0}")]
77 ContractReadError(#[from] ContractError),
78 #[error("Missing deploy mode argument")]
79 MissingDeployMode
80}
81
82impl From<OdraError> for DeployError {
83 fn from(err: OdraError) -> Self {
84 DeployError::OdraError {
85 message: format!("{:?}", err)
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use crate::test_utils;
93
94 use super::*;
95 use odra::host::HostEnv;
96
97 struct MockDeployScript;
98
99 impl DeployScript for MockDeployScript {
100 fn deploy(
101 &self,
102 _env: &HostEnv,
103 _container: &mut DeployedContractsContainer
104 ) -> core::result::Result<(), DeployError> {
105 Ok(())
106 }
107 }
108
109 #[test]
110 fn deploy_cmd_run() {
111 let env = test_utils::mock_host_env();
113 let cmd = DeployCmd::new(MockDeployScript);
114 let mut container = test_utils::mock_contracts_container();
115 let command: Command = (&cmd).into();
116 let arg_matches = command.try_get_matches_from(vec!["test"]).unwrap();
117 let result = cmd.run(
118 &env,
119 &arg_matches,
120 &CustomTypeSet::default(),
121 &mut container
122 );
123 assert!(result.is_ok());
124 }
125
126 #[test]
127 fn parsing_deploy_cmd() {
128 let cmd = DeployCmd::new(MockDeployScript);
130 let command: Command = (&cmd).into();
131 assert_eq!(command.get_name(), DEPLOY_SUBCOMMAND);
132 assert!(command.get_about().is_some());
133 }
134
135 #[test]
136 fn deploy_accepts_mode_arg() {
137 let cmd = DeployCmd::new(MockDeployScript);
138 let command: Command = (&cmd).into();
139
140 let result =
141 command
142 .clone()
143 .try_get_matches_from(vec!["test", "--deploy-mode", "override"]);
144 assert!(result.is_ok());
145
146 let result = command
147 .clone()
148 .try_get_matches_from(vec!["test", "--deploy-mode", "default"]);
149 assert!(result.is_ok());
150
151 let result = command
152 .clone()
153 .try_get_matches_from(vec!["test", "--deploy-mode", "archive"]);
154 assert!(result.is_ok());
155
156 let result = command
157 .clone()
158 .try_get_matches_from(vec!["test", "--deploy-mode", "abc"]);
159 assert!(result.is_err());
160
161 let result = command.try_get_matches_from(vec!["test"]);
162 assert!(result.is_ok());
163
164 let command: Command = (&cmd).into();
165 assert_eq!(command.get_arguments().count(), 1);
166 }
167}