soroban_cli/commands/contract/
fetch.rs1use std::convert::Infallible;
2
3use std::io::Write;
4use std::path::{Path, PathBuf};
5use std::str::FromStr;
6use std::{fmt::Debug, fs, io};
7
8use clap::{arg, command, Parser};
9
10use crate::{
11 commands::{global, NetworkRunnable},
12 config::{
13 self, locator,
14 network::{self, Network},
15 },
16 wasm, Pwd,
17};
18
19#[derive(Parser, Debug, Default, Clone)]
20#[allow(clippy::struct_excessive_bools)]
21#[group(skip)]
22pub struct Cmd {
23 #[arg(long = "id", env = "STELLAR_CONTRACT_ID")]
25 pub contract_id: config::UnresolvedContract,
26 #[arg(long, short = 'o')]
28 pub out_file: Option<std::path::PathBuf>,
29 #[command(flatten)]
30 pub locator: locator::Args,
31 #[command(flatten)]
32 pub network: network::Args,
33}
34
35impl FromStr for Cmd {
36 type Err = clap::error::Error;
37
38 fn from_str(s: &str) -> Result<Self, Self::Err> {
39 use clap::{CommandFactory, FromArgMatches};
40 Self::from_arg_matches_mut(&mut Self::command().get_matches_from(s.split_whitespace()))
41 }
42}
43
44impl Pwd for Cmd {
45 fn set_pwd(&mut self, pwd: &Path) {
46 self.locator.set_pwd(pwd);
47 }
48}
49
50#[derive(thiserror::Error, Debug)]
51pub enum Error {
52 #[error(transparent)]
53 Config(#[from] config::Error),
54 #[error(transparent)]
55 Locator(#[from] locator::Error),
56 #[error(transparent)]
57 Io(#[from] std::io::Error),
58 #[error("reading file {0:?}: {1}")]
59 CannotWriteContractFile(PathBuf, io::Error),
60 #[error(transparent)]
61 Network(#[from] network::Error),
62 #[error("cannot create contract directory for {0:?}")]
63 CannotCreateContractDir(PathBuf),
64 #[error(transparent)]
65 Wasm(#[from] wasm::Error),
66}
67
68impl From<Infallible> for Error {
69 fn from(_: Infallible) -> Self {
70 unreachable!()
71 }
72}
73
74impl Cmd {
75 pub async fn run(&self) -> Result<(), Error> {
76 let bytes = self.get_bytes().await?;
77 if let Some(out_file) = &self.out_file {
78 if let Some(parent) = out_file.parent() {
79 if !parent.exists() {
80 fs::create_dir_all(parent)
81 .map_err(|_| Error::CannotCreateContractDir(out_file.clone()))?;
82 }
83 }
84 fs::write(out_file, bytes)
85 .map_err(|io| Error::CannotWriteContractFile(out_file.clone(), io))
86 } else {
87 let stdout = std::io::stdout();
88 let mut handle = stdout.lock();
89 handle.write_all(&bytes)?;
90 handle.flush()?;
91 Ok(())
92 }
93 }
94
95 pub async fn get_bytes(&self) -> Result<Vec<u8>, Error> {
96 self.run_against_rpc_server(None, None).await
97 }
98
99 pub fn network(&self) -> Result<Network, Error> {
100 Ok(self.network.get(&self.locator)?)
101 }
102}
103
104#[async_trait::async_trait]
105impl NetworkRunnable for Cmd {
106 type Error = Error;
107 type Result = Vec<u8>;
108 async fn run_against_rpc_server(
109 &self,
110 _args: Option<&global::Args>,
111 config: Option<&config::Args>,
112 ) -> Result<Vec<u8>, Error> {
113 let network = config.map_or_else(|| self.network(), |c| Ok(c.get_network()?))?;
114 Ok(wasm::fetch_from_contract(
115 &self
116 .contract_id
117 .resolve_contract_id(&self.locator, &network.network_passphrase)?,
118 &network,
119 )
120 .await?)
121 }
122}