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 crate::log("Deployment completed successfully.");
48 Ok(())
49 }
50}
51
52impl From<&DeployCmd> for Command {
53 fn from(_value: &DeployCmd) -> Self {
54 Command::new(DEPLOY_SUBCOMMAND)
55 .arg(Arg::DeployMode)
56 .about("Runs the deploy script")
57 }
58}
59
60pub trait DeployScript {
65 fn deploy(
66 &self,
67 env: &HostEnv,
68 container: &mut DeployedContractsContainer
69 ) -> core::result::Result<(), DeployError>;
70}
71
72#[derive(Debug, Error)]
74pub enum DeployError {
75 #[error("Deploy error: {message}")]
76 OdraError { message: String },
77 #[error("Contract read error: {0}")]
78 ContractReadError(#[from] ContractError),
79 #[error("Missing deploy mode argument")]
80 MissingDeployMode
81}
82
83impl From<OdraError> for DeployError {
84 fn from(err: OdraError) -> Self {
85 DeployError::OdraError {
86 message: format!("{err:?}")
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use crate::test_utils;
94
95 use super::*;
96 use odra::host::HostEnv;
97
98 struct MockDeployScript;
99
100 impl DeployScript for MockDeployScript {
101 fn deploy(
102 &self,
103 _env: &HostEnv,
104 _container: &mut DeployedContractsContainer
105 ) -> core::result::Result<(), DeployError> {
106 Ok(())
107 }
108 }
109
110 #[test]
111 fn deploy_cmd_run() {
112 let env = test_utils::mock_host_env();
114 let cmd = DeployCmd::new(MockDeployScript);
115 let mut container = test_utils::mock_contracts_container();
116 let command: Command = (&cmd).into();
117 let arg_matches = command.try_get_matches_from(vec!["test"]).unwrap();
118 let result = cmd.run(
119 &env,
120 &arg_matches,
121 &CustomTypeSet::default(),
122 &mut container
123 );
124 assert!(result.is_ok());
125 }
126
127 #[test]
128 fn parsing_deploy_cmd() {
129 let cmd = DeployCmd::new(MockDeployScript);
131 let command: Command = (&cmd).into();
132 assert_eq!(command.get_name(), DEPLOY_SUBCOMMAND);
133 assert!(command.get_about().is_some());
134 }
135
136 #[test]
137 fn deploy_accepts_mode_arg() {
138 let cmd = DeployCmd::new(MockDeployScript);
139 let command: Command = (&cmd).into();
140
141 let result =
142 command
143 .clone()
144 .try_get_matches_from(vec!["test", "--deploy-mode", "override"]);
145 assert!(result.is_ok());
146
147 let result = command
148 .clone()
149 .try_get_matches_from(vec!["test", "--deploy-mode", "default"]);
150 assert!(result.is_ok());
151
152 let result = command
153 .clone()
154 .try_get_matches_from(vec!["test", "--deploy-mode", "archive"]);
155 assert!(result.is_ok());
156
157 let result = command
158 .clone()
159 .try_get_matches_from(vec!["test", "--deploy-mode", "abc"]);
160 assert!(result.is_err());
161
162 let result = command.try_get_matches_from(vec!["test"]);
163 assert!(result.is_ok());
164
165 let command: Command = (&cmd).into();
166 assert_eq!(command.get_arguments().count(), 1);
167 }
168}