cargo_lambda_deploy/
lib.rs1use aws_smithy_types::retry::{RetryConfig, RetryMode};
2use cargo_lambda_build::{BinaryArchive, BinaryData, create_binary_archive, zip_binary};
3use cargo_lambda_interactive::progress::Progress;
4use cargo_lambda_metadata::cargo::{
5 CargoMetadata,
6 deploy::{Deploy, OutputFormat},
7 main_binary_from_metadata,
8};
9use miette::{IntoDiagnostic, Result, WrapErr};
10use serde::Serialize;
11use serde_json::ser::to_string_pretty;
12use std::time::Duration;
13
14mod dry;
15mod extensions;
16mod functions;
17mod roles;
18
19#[derive(Serialize)]
20#[serde(untagged)]
21#[allow(clippy::large_enum_variant)]
22enum DeployResult {
23 Extension(extensions::DeployOutput),
24 Function(functions::DeployOutput),
25 Dry(dry::DeployOutput),
26}
27
28impl std::fmt::Display for DeployResult {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 DeployResult::Extension(o) => o.fmt(f),
32 DeployResult::Function(o) => o.fmt(f),
33 DeployResult::Dry(o) => o.fmt(f),
34 }
35 }
36}
37
38#[tracing::instrument(target = "cargo_lambda")]
39pub async fn run(config: &Deploy, metadata: &CargoMetadata) -> Result<()> {
40 tracing::trace!("deploying project");
41
42 if config.function_config.enable_function_url && config.function_config.disable_function_url {
43 return Err(miette::miette!(
44 "invalid options: --enable-function-url and --disable-function-url cannot be set together"
45 ));
46 }
47
48 let progress = Progress::start("loading binary data");
49 let (name, archive) = match load_archive(config, metadata) {
50 Ok(arc) => arc,
51 Err(err) => {
52 progress.finish_and_clear();
53 return Err(err);
54 }
55 };
56
57 let retry = RetryConfig::standard()
58 .with_retry_mode(RetryMode::Adaptive)
59 .with_max_attempts(3)
60 .with_initial_backoff(Duration::from_secs(5));
61
62 let sdk_config = config.remote_config.sdk_config(Some(retry)).await;
63
64 let result = if config.dry {
65 dry::DeployOutput::new(config, &name, &archive).map(DeployResult::Dry)
66 } else if config.extension {
67 extensions::deploy(config, &name, &sdk_config, &archive, &progress)
68 .await
69 .map(DeployResult::Extension)
70 } else {
71 functions::deploy(config, &name, &sdk_config, &archive, &progress)
72 .await
73 .map(DeployResult::Function)
74 };
75
76 progress.finish_and_clear();
77 let output = result?;
78
79 match &config.output_format() {
80 OutputFormat::Text => println!("{output}"),
81 OutputFormat::Json => {
82 let text = to_string_pretty(&output)
83 .into_diagnostic()
84 .wrap_err("failed to serialize output into json")?;
85 println!("{text}")
86 }
87 }
88
89 Ok(())
90}
91
92fn load_archive(config: &Deploy, metadata: &CargoMetadata) -> Result<(String, BinaryArchive)> {
93 match &config.binary_path {
94 Some(bp) if bp.is_dir() => Err(miette::miette!("invalid file {:?}", bp)),
95 Some(bp) => {
96 let name = match &config.name {
97 Some(name) => name.clone(),
98 None => bp
99 .file_name()
100 .and_then(|s| s.to_str())
101 .map(String::from)
102 .ok_or_else(|| miette::miette!("invalid binary path {:?}", bp))?,
103 };
104
105 let destination = bp
106 .parent()
107 .ok_or_else(|| miette::miette!("invalid binary path {:?}", bp))?;
108
109 let data = BinaryData::new(&name, config.extension, config.internal);
110 let arc = zip_binary(bp, destination, &data, config.include.clone())?;
111 Ok((name, arc))
112 }
113 None => {
114 let name = match (&config.name, &config.binary_name) {
115 (Some(name), _) => name.clone(),
116 (None, Some(bn)) => bn.clone(),
117 (None, None) => main_binary_from_metadata(metadata)?,
118 };
119 let binary_name = binary_name_or_default(config, &name);
120 let data = BinaryData::new(&binary_name, config.extension, config.internal);
121
122 let arc = create_binary_archive(
123 Some(metadata),
124 &config.lambda_dir,
125 &data,
126 config.include.clone(),
127 )?;
128 Ok((name, arc))
129 }
130 }
131}
132
133pub(crate) fn binary_name_or_default(config: &Deploy, name: &str) -> String {
134 config
135 .binary_name
136 .clone()
137 .unwrap_or_else(|| name.to_string())
138}
139
140#[cfg(test)]
141mod tests {
142 use assertables::assert_contains;
143 use std::path::PathBuf;
144
145 use cargo_lambda_metadata::cargo::load_metadata;
146
147 use super::*;
148
149 #[test]
150 fn test_load_archive_from_binary_path() {
151 let mut config = Deploy::default();
152 config.binary_path = Some(PathBuf::from("../../tests/binaries/binary-x86-64"));
153 config.include = Some(vec!["src".into()]);
154
155 let metadata = load_metadata("../../tests/fixtures/examples-package/Cargo.toml").unwrap();
156 let (name, archive) = load_archive(&config, &metadata).unwrap();
157 assert_eq!(name, "binary-x86-64");
158
159 let files = archive.list().unwrap();
160 assert_contains!(files, &"bootstrap".to_string());
161 assert_contains!(files, &"src/dry.rs".to_string());
162 assert_contains!(files, &"src/extensions.rs".to_string());
163 assert_contains!(files, &"src/functions.rs".to_string());
164 assert_contains!(files, &"src/lib.rs".to_string());
165 assert_contains!(files, &"src/roles.rs".to_string());
166 }
167}