1use crate::Config;
2use crate::callback::event_manager::EventManager;
3use crate::callback::filter_manager::FilterManager;
4use crate::callback::preprocess_manager::PreprocessManager;
5#[allow(unused_imports)]
6use crate::config::args::value_parser::{
7 canned_acl, checksum_algorithm, file_exist, human_bytes, metadata, sse, storage_class,
8 storage_path, tagging, url,
9};
10use crate::config::{
11 CLITimeoutConfig, ClientConfig, FilterConfig, ForceRetryConfig, RetryConfig, TracingConfig,
12 TransferConfig,
13};
14#[allow(unused_imports)]
15use crate::types::event_callback::EventType;
16use crate::types::{
17 AccessKeys, ClientConfigLocation, S3Credentials, SseCustomerKey, SseKmsKeyId, StoragePath,
18};
19use aws_sdk_s3::types::{
20 ChecksumAlgorithm, ChecksumMode, ObjectCannedAcl, RequestPayer, ServerSideEncryption,
21 StorageClass,
22};
23use aws_smithy_types::checksum_config::RequestChecksumCalculation;
24use cfg_if::cfg_if;
25use chrono::{DateTime, Utc};
26use clap::Parser;
27use clap::builder::{ArgPredicate, NonEmptyStringValueParser};
28use clap_verbosity_flag::{Verbosity, WarnLevel};
29use fancy_regex::Regex;
30#[cfg(feature = "version")]
31use shadow_rs::shadow;
32use std::ffi::OsString;
33use std::path::PathBuf;
34use std::str::FromStr;
35use std::sync::Arc;
36use tokio::sync::Semaphore;
37
38mod tests;
39mod value_parser;
40
41const EXPRESS_ONEZONE_STORAGE_SUFFIX: &str = "--x-s3";
42
43const DEFAULT_WORKER_SIZE: u16 = 16;
44const DEFAULT_AWS_MAX_ATTEMPTS: u32 = 10;
45const DEFAULT_FORCE_RETRY_COUNT: u32 = 5;
46const DEFAULT_FORCE_RETRY_INTERVAL_MILLISECONDS: u64 = 1000;
47const DEFAULT_INITIAL_BACKOFF_MILLISECONDS: u64 = 100;
48const DEFAULT_JSON_TRACING: bool = false;
49const DEFAULT_AWS_SDK_TRACING: bool = false;
50const DEFAULT_SPAN_EVENTS_TRACING: bool = false;
51const DEFAULT_DISABLE_COLOR_TRACING: bool = false;
52const DEFAULT_TRACING_STDERR: bool = false;
53const DEFAULT_MULTIPART_THRESHOLD: &str = "8MiB";
54const DEFAULT_MULTIPART_CHUNKSIZE: &str = "8MiB";
55const DEFAULT_AUTO_CHUNKSIZE: bool = false;
56const DEFAULT_NO_SYNC_SYSTEM_METADATA: bool = false;
57const DEFAULT_NO_SYNC_USER_DEFINED_METADATA: bool = false;
58const DEFAULT_WARN_AS_ERROR: bool = false;
59const DEFAULT_IGNORE_SYMLINKS: bool = false;
60const DEFAULT_FORCE_PATH_STYLE: bool = false;
61const DEFAULT_HEAD_EACH_TARGET: bool = false;
62const DEFAULT_ENABLE_VERSIONING: bool = false;
63const DEFAULT_REMOVE_MODIFIED_FILTER: bool = false;
64const DEFAULT_CHECK_SIZE: bool = false;
65const DEFAULT_CHECK_ETAG: bool = false;
66const DEFAULT_CHECK_MTIME_AND_ETAG: bool = false;
67const DEFAULT_SYNC_WITH_DELETE: bool = false;
68const DEFAULT_DISABLE_TAGGING: bool = false;
69const DEFAULT_SYNC_LATEST_TAGGING: bool = false;
70const DEFAULT_NO_GUESS_MIME_TYPE: bool = false;
71const DEFAULT_SERVER_SIDE_COPY: bool = false;
72const DEFAULT_DISABLE_MULTIPART_VERIFY: bool = false;
73const DEFAULT_DISABLE_ETAG_VERIFY: bool = false;
74const DEFAULT_DISABLE_ADDITIONAL_CHECKSUM_VERIFY: bool = false;
75const DEFAULT_ENABLE_ADDITIONAL_CHECKSUM: bool = false;
76const DEFAULT_DRY_RUN: bool = false;
77const DEFAULT_MAX_KEYS: i32 = 1000;
78const DEFAULT_PUT_LAST_MODIFIED_METADATA: bool = false;
79const DEFAULT_DISABLE_STALLED_STREAM_PROTECTION: bool = false;
80const DEFAULT_DISABLE_PAYLOAD_SIGNING: bool = false;
81const DEFAULT_DISABLE_CONTENT_MD5_HEADER: bool = false;
82const DEFAULT_DELETE_EXCLUDED: bool = false;
83const DEFAULT_FULL_OBJECT_CHECKSUM: bool = false;
84const DEFAULT_DISABLE_EXPRESS_ONE_ZONE_ADDITIONAL_CHECKSUM: bool = false;
85const DEFAULT_MAX_PARALLEL_MULTIPART_UPLOADS: u16 = 16;
86const DEFAULT_MAX_PARALLEL_LISTINGS: u16 = 16;
87const DEFAULT_OBJECT_LISTING_QUEUE_SIZE: u32 = 200000;
88const DEFAULT_PARALLEL_LISTING_MAX_DEPTH: u16 = 2;
89const DEFAULT_ALLOW_PARALLEL_LISTINGS_IN_EXPRESS_ONE_ZONE: bool = false;
90const DEFAULT_ACCELERATE: bool = false;
91const DEFAULT_REQUEST_PAYER: bool = false;
92const DEFAULT_REPORT_SYNC_STATUS: bool = false;
93const DEFAULT_REPORT_METADATA_SYNC_STATUS: bool = false;
94const DEFAULT_REPORT_TAGGING_SYNC_STATUS: bool = false;
95const DEFAULT_REPORT_ANNOTATIONS_SYNC_STATUS: bool = false;
96#[allow(dead_code)]
97const DEFAULT_ALLOW_LUA_OS_LIBRARY: bool = false;
98#[allow(dead_code)]
99const DEFAULT_ALLOW_LUA_UNSAFE_VM: bool = false;
100#[allow(dead_code)]
101const DEFAULT_LUA_VM_MEMORY_LIMIT: &str = "64MiB";
102#[allow(dead_code)]
103const DEFAULT_LUA_CALLBACK_TIMEOUT_MILLISECONDS: &str = "10000";
104const DEFAULT_SHOW_NO_PROGRESS: bool = false;
105const DEFAULT_IF_MATCH: bool = false;
106const DEFAULT_IF_NONE_MATCH: bool = false;
107const DEFAULT_COPY_SOURCE_IF_MATCH: bool = false;
108const DEFAULT_IGNORE_GLACIER_WARNINGS: bool = false;
109const DEFAULT_ENABLE_SYNC_OBJECT_ANNOTATION: bool = false;
110const DEFAULT_DISABLE_CHECK_ANNOTATION_ETAG: bool = false;
111const DEFAULT_SYNC_LATEST_OBJECT_ANNOTATION: bool = false;
112
113const NO_S3_STORAGE_SPECIFIED: &str = "either SOURCE or TARGET must be s3://\n";
114const LOCAL_STORAGE_SPECIFIED: &str = "with --enable-versioning/--sync-latest-tagging/--copy-source-if-match, both storage must be s3://\n";
115const LOCAL_STORAGE_SPECIFIED_FOR_OBJECT_ANNOTATION: &str = "with --enable-sync-object-annotations/--sync-latest-object-annotations/--report-annotations-sync-status, both storage must be s3://\n";
116const VERSIONING_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE: &str =
117 "--enable-versioning is not supported with express onezone storage class\n";
118const EXPRESS_ONEZONE_SPECIFIED_FOR_OBJECT_ANNOTATION: &str = "--enable-sync-object-annotations/--sync-latest-object-annotations/--report-annotations-sync-status are not supported with express onezone storage class\n";
119const LOCAL_STORAGE_SPECIFIED_WITH_STORAGE_CLASS: &str =
120 "with --storage-class, target storage must be s3://\n";
121const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_SSE: &str =
122 "with --sse/--sse-kms-key-id, target storage must be s3://\n";
123const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_ACL: &str = "with --acl, target storage must be s3://\n";
124const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_ENABLE_ADDITIONAL_CHECKSUM: &str =
125 "with --enable-additional-checksum, source storage must be s3://\n";
126const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_ADDITIONAL_CHECKSUM_ALGORITHM: &str =
127 "with --additional-checksum-algorithm, target storage must be s3://\n";
128const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_AUTO_CHUNKSIZE: &str =
129 "with --auto-chunksize, source storage must be s3://\n";
130const SOURCE_REMOTE_STORAGE_SPECIFIED_WITH_IGNORE_SYMLINKS: &str =
131 "with --ignore-symlinks, source storage must be local storage\n";
132const SOURCE_REMOTE_STORAGE_SPECIFIED_WITH_NO_GUESS_MIME_TYPE: &str =
133 "with --no-guess-mime-type, source storage must be local storage\n";
134const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_METADATA_OPTION: &str =
135 "with metadata related option, target storage must be s3://\n";
136const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_ENDPOINT_URL: &str =
137 "with --source-endpoint-url, source storage must be s3://\n";
138const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_ENDPOINT_URL: &str =
139 "with --target-endpoint-url, target storage must be s3://\n";
140const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_NO_SIGN_REQUEST: &str =
141 "with --source-no-sign-request, source storage must be s3://\n";
142const CHECK_SIZE_CONFLICT: &str =
143 "--head-each-target is required for --check-size, or remove --remove-modified-filter\n";
144
145const CHECK_ETAG_CONFLICT: &str =
146 "--head-each-target is required for --check-etag, or remove --remove-modified-filter\n";
147const CHECK_ETAG_CONFLICT_SSE_KMS: &str =
148 "--check-etag is not supported with --sse aws:kms | aws:kms:dsse \n";
149const CHECK_ETAG_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE: &str =
150 "--check-etag is not supported with express onezone storage class\n";
151
152const CHECK_MTIME_AND_ETAG_CONFLICT_SSE_KMS: &str =
153 "--check-mtime-and-etag is not supported with --sse aws:kms | aws:kms:dsse \n";
154const CHECK_MTIME_AND_ETAG_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE: &str =
155 "--check-mtime-and-etag is not supported with express onezone storage class\n";
156const SOURCE_LOCAL_STORAGE_FILE_WITH_DELETE_OPTION: &str =
157 "with --delete, source storage must be directory\n";
158const SOURCE_LOCAL_STORAGE_PATH_NOT_FOUND: &str = "source file/directory not found\n";
159const TARGET_LOCAL_STORAGE_INVALID: &str = "invalid target path\n";
160const SSE_KMS_KEY_ID_ARGUMENTS_CONFLICT: &str =
161 "--sse-kms-key-id must be used with --sse aws:kms | aws:kms:dsse\n";
162const LOCAL_STORAGE_SPECIFIED_WITH_SSE_C: &str =
163 "with --source-sse-c/--target-sse-c, remote storage must be s3://\n";
164const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_DISABLE_PAYLOAD_SIGNING: &str =
165 "with --disable-payload-signing, target storage must be s3://\n";
166const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_DISABLE_CONTENT_MD5_HEADER: &str =
167 "with --disable-content-md5-header, target storage must be s3://\n";
168const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_FULL_OBJECT_CHECKSUM: &str =
169 "with --full-object-checksum, target storage must be s3://\n";
170const FULL_OBJECT_CHECKSUM_NOT_SUPPORTED: &str =
171 "Only CRC32/CRC32C/CRC64NVME supports full object checksum\n";
172const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_ACCELERATE: &str =
173 "with --source-accelerate, source storage must be s3://\n";
174const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_ACCELERATE: &str =
175 "with --target-accelerate, target storage must be s3://\n";
176const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_REQUEST_PAYER: &str =
177 "with --source-request-payer, source storage must be s3://\n";
178const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_REQUEST_PAYER: &str =
179 "with --target-request-payer, target storage must be s3://\n";
180const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_SERVER_SIDE_COPY: &str =
181 "with --server-side-copy, source storage must be s3://\n";
182const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_SERVER_SIDE_COPY: &str =
183 "with --server-side-copy, target storage must be s3://\n";
184
185const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_FILTER_INCLUDE_METADATA_REGEX: &str =
186 "with --filter-include-metadata-regex, source storage must be s3://\n";
187const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_FILTER_EXCLUDE_METADATA_REGEX: &str =
188 "with --filter-exclude-metadata-regex, source storage must be s3://\n";
189
190const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_FILTER_INCLUDE_TAG_REGEX: &str =
191 "with --filter-include-tag-regex, source storage must be s3://\n";
192const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_FILTER_EXCLUDE_TAG_REGEX: &str =
193 "with --filter-exclude-tag-regex, source storage must be s3://\n";
194
195const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_NO_SYNC_SYSTEM_METADATA: &str =
196 "with --no-sync-system-metadata, source storage must be s3://\n";
197const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_NO_SYNC_SYSTEM_METADATA: &str =
198 "with --no-sync-system-metadata, target storage must be s3://\n";
199const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_NO_SYNC_USER_DEFINED_METADATA: &str =
200 "with --no-sync-user-defined-metadata, source storage must be s3://\n";
201const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_NO_SYNC_USER_DEFINED_METADATA: &str =
202 "with --no-sync-user-defined-metadata, target storage must be s3://\n";
203const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_POINT_IN_TIME: &str =
204 "with --point-in-time, source storage must be s3://\n";
205const POINT_IN_TIME_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE: &str =
206 "--point-in-time is not supported with express onezone storage class\n";
207const POINT_IN_TIME_VALUE_INVALID: &str = "--point-in-time value is later than current time\n";
208const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_REPORT_METADATA_SYNC_STATUS: &str =
209 "with --report-metadata-sync-status, source storage must be s3://\n";
210const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_REPORT_METADATA_SYNC_STATUS: &str =
211 "with --report-metadata-sync-status, target storage must be s3://\n";
212const SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_REPORT_TAGGING_SYNC_STATUS: &str =
213 "with --report-tagging-sync-status, source storage must be s3://\n";
214const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_REPORT_TAGGING_SYNC_STATUS: &str =
215 "with --report-tagging-sync-status, target storage must be s3://\n";
216const NO_SOURCE_CREDENTIAL_REQUIRED: &str = "no source credential required\n";
217const NO_TARGET_CREDENTIAL_REQUIRED: &str = "no target credential required\n";
218#[allow(dead_code)]
219const LUA_SCRIPT_LOAD_ERROR: &str = "Failed to load and compile Lua script callback: ";
220const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_IF_MATCH: &str =
221 "with --if-match, target storage must be s3://\n";
222const IF_MATCH_CONFLICT: &str =
223 "--head-each-target is required for --if-match, or remove --remove-modified-filter\n";
224const TARGET_LOCAL_STORAGE_SPECIFIED_WITH_IF_NONE_MATCH: &str =
225 "with --if-none-match, target storage must be s3://\n";
226
227#[cfg(feature = "version")]
228shadow!(build);
229
230#[derive(Parser, Clone, Debug)]
231#[cfg_attr(feature = "version", command(version=format!("{} ({} {}), {}", build::PKG_VERSION, build::SHORT_COMMIT, build::BUILD_TARGET, build::RUST_VERSION)))]
232pub struct CLIArgs {
233 #[arg(help = "s3://<BUCKET_NAME>[/prefix] or local path", value_parser = storage_path::check_storage_path, default_value_if("auto_complete_shell", ArgPredicate::IsPresent, "s3://ignored"), required = false)]
234 source: String,
235
236 #[arg(help = "s3://<BUCKET_NAME>[/prefix] or local path", value_parser = storage_path::check_storage_path, default_value_if("auto_complete_shell", ArgPredicate::IsPresent, "s3://ignored"), required = false)]
237 target: String,
238
239 #[arg(long, env, default_value_t = DEFAULT_DRY_RUN, help_heading = "General")]
241 dry_run: bool,
242
243 #[arg(long, env, default_value_t = DEFAULT_SHOW_NO_PROGRESS, help_heading = "General")]
245 show_no_progress: bool,
246
247 #[arg(long, env, default_value_t = DEFAULT_SERVER_SIDE_COPY, help_heading = "General",
248 long_help = r#"Use server-side copy. This option is only available both source and target are S3 storage.
249It cannot work with between different object storages or regions."#)]
250 server_side_copy: bool,
251
252 #[arg(long, env, value_name = "FILE", help_heading = "AWS Configuration")]
254 aws_config_file: Option<PathBuf>,
255
256 #[arg(long, env, value_name = "FILE", help_heading = "AWS Configuration")]
258 aws_shared_credentials_file: Option<PathBuf>,
259
260 #[arg(long, env, conflicts_with_all = ["source_access_key", "source_secret_access_key", "source_session_token"], help_heading = "AWS Configuration")]
262 source_profile: Option<String>,
263
264 #[arg(long, env, hide_env_values = true, conflicts_with_all = ["source_profile"], requires = "source_secret_access_key", help_heading = "AWS Configuration")]
266 source_access_key: Option<String>,
267
268 #[arg(long, env, hide_env_values = true, conflicts_with_all = ["source_profile"], requires = "source_access_key", help_heading = "AWS Configuration")]
270 source_secret_access_key: Option<String>,
271
272 #[arg(long, env, hide_env_values = true, conflicts_with_all = ["source_profile"], requires = "source_access_key", help_heading = "AWS Configuration")]
274 source_session_token: Option<String>,
275
276 #[arg(long, env, value_parser = NonEmptyStringValueParser::new(), help_heading = "Source Options")]
278 source_region: Option<String>,
279
280 #[arg(long, env, value_parser = url::check_scheme, help_heading = "Source Options")]
282 source_endpoint_url: Option<String>,
283
284 #[arg(long, env, default_value_t = DEFAULT_ACCELERATE, help_heading = "Source Options")]
286 source_accelerate: bool,
287
288 #[arg(long, env, default_value_t = DEFAULT_REQUEST_PAYER, help_heading = "Source Options")]
290 source_request_payer: bool,
291
292 #[arg(
294 long, env, default_value_t = false,
295 conflicts_with_all = [
296 "source_profile",
297 "source_access_key",
298 "source_secret_access_key",
299 "source_session_token",
300 "source_request_payer",
301 ],
302 help_heading = "AWS Configuration",
303 )]
304 source_no_sign_request: bool,
305
306 #[arg(long, env, default_value_t = DEFAULT_FORCE_PATH_STYLE, help_heading = "Source Options")]
308 source_force_path_style: bool,
309
310 #[arg(long, env, conflicts_with_all = ["target_access_key", "target_secret_access_key", "target_session_token"], help_heading = "AWS Configuration")]
312 target_profile: Option<String>,
313
314 #[arg(long, env, hide_env_values = true, conflicts_with_all = ["target_profile"], requires = "target_secret_access_key", help_heading = "AWS Configuration")]
316 target_access_key: Option<String>,
317
318 #[arg(long, env, hide_env_values = true, conflicts_with_all = ["target_profile"], requires = "target_access_key", help_heading = "AWS Configuration")]
320 target_secret_access_key: Option<String>,
321
322 #[arg(long, env, hide_env_values = true, conflicts_with_all = ["target_profile"], requires = "target_access_key", help_heading = "AWS Configuration")]
324 target_session_token: Option<String>,
325
326 #[arg(long, env, value_parser = NonEmptyStringValueParser::new(), help_heading = "Target Options")]
328 target_region: Option<String>,
329
330 #[arg(long, env, value_parser = url::check_scheme, help_heading = "Target Options")]
332 target_endpoint_url: Option<String>,
333
334 #[arg(long, env, default_value_t = DEFAULT_ACCELERATE, help_heading = "Target Options")]
336 target_accelerate: bool,
337
338 #[arg(long, env, default_value_t = DEFAULT_REQUEST_PAYER,help_heading = "Target Options")]
340 target_request_payer: bool,
341
342 #[arg(long, env, default_value_t = DEFAULT_FORCE_PATH_STYLE, help_heading = "Target Options")]
344 target_force_path_style: bool,
345
346 #[arg(long, env, value_parser = storage_class::parse_storage_class, help_heading = "Target Options",
347 long_help = r#"Type of storage to use for the target object.
348Valid choices: STANDARD | REDUCED_REDUNDANCY | STANDARD_IA | ONEZONE_IA | INTELLIGENT_TIERING | GLACIER |
349 DEEP_ARCHIVE | GLACIER_IR | EXPRESS_ONEZONE"#)]
350 storage_class: Option<String>,
351
352 #[arg(
353 long,
354 env,
355 help_heading = "Filtering",
356 long_help = r#"Sync only objects older than given time (RFC3339 datetime).
357Example: 2023-02-19T12:00:00Z"#
358 )]
359 filter_mtime_before: Option<DateTime<Utc>>,
360
361 #[arg(
362 long,
363 env,
364 help_heading = "Filtering",
365 long_help = r#"Sync only objects newer than OR EQUAL TO given time (RFC3339 datetime).
366Example: 2023-02-19T12:00:00Z"#
367 )]
368 filter_mtime_after: Option<DateTime<Utc>>,
369
370 #[arg(long, env, value_parser = human_bytes::check_human_bytes_without_limit, help_heading = "Filtering", long_help=r#"Sync only objects smaller than given size.
371Allow suffixes: KB, KiB, MB, MiB, GB, GiB, TB, TiB"#)]
372 filter_smaller_size: Option<String>,
373
374 #[arg(long, env, value_parser = human_bytes::check_human_bytes_without_limit, help_heading = "Filtering", long_help=r#"Sync only objects larger than OR EQUAL TO given size.
375Allow suffixes: KB, KiB, MB, MiB, GB, GiB, TB, TiB"#)]
376 filter_larger_size: Option<String>,
377
378 #[arg(long, env, value_parser = crate::config::args::value_parser::regex::parse_regex, help_heading = "Filtering")]
380 filter_include_regex: Option<String>,
381
382 #[arg(long, env, value_parser = crate::config::args::value_parser::regex::parse_regex, help_heading = "Filtering")]
384 filter_exclude_regex: Option<String>,
385
386 #[arg(long, env, value_parser = crate::config::args::value_parser::regex::parse_regex, help_heading = "Filtering",
387 long_help = r#"Sync only objects that have Content-Type matching a given regular expression.
388If the source is local storage, Content-Type is guessed by the file extension,
389Unless --no-guess-mime-type is specified.
390It may take an extra API call to get Content-Type of the object.
391"#)]
392 filter_include_content_type_regex: Option<String>,
393
394 #[arg(long, env, value_parser = crate::config::args::value_parser::regex::parse_regex, help_heading = "Filtering",
395 long_help=r#"Do not sync objects that have Content-Type matching a given regular expression.
396If the source is local storage, Content-Type is guessed by the file extension,
397Unless --no-guess-mime-type is specified.
398It may take an extra API call to get Content-Type of the object.
399"#)]
400 filter_exclude_content_type_regex: Option<String>,
401
402 #[arg(long, env, value_parser = crate::config::args::value_parser::regex::parse_regex, help_heading = "Filtering",
403 long_help=r#"Sync only objects that have metadata matching a given regular expression.
404Keys(lowercase) must be sorted in alphabetical order, and comma separated.
405This filter is applied after all other filters(except tag filters).
406It may take an extra API call to get metadata of the object.
407
408Example: "key1=(value1|value2),key2=value2""#)]
409 filter_include_metadata_regex: Option<String>,
410
411 #[arg(long, env, value_parser = crate::config::args::value_parser::regex::parse_regex, help_heading = "Filtering",
412 long_help=r#"Do not sync objects that have metadata matching a given regular expression.
413Keys(lowercase) must be sorted in alphabetical order, and comma separated.
414This filter is applied after all other filters(except tag filters).
415It may take an extra API call to get metadata of the object.
416
417Example: "key1=(value1|value2),key2=value2""#)]
418 filter_exclude_metadata_regex: Option<String>,
419
420 #[arg(long, env, value_parser = crate::config::args::value_parser::regex::parse_regex, help_heading = "Filtering",
421 long_help=r#"Sync only objects that have tag matching a given regular expression.
422Keys must be sorted in alphabetical order, and '&' separated.
423This filter is applied after all other filters.
424It takes an extra API call to get tags of the object.
425
426Example: "key1=(value1|value2)&key2=value2""#)]
427 filter_include_tag_regex: Option<String>,
428
429 #[arg(long, env, value_parser = crate::config::args::value_parser::regex::parse_regex, help_heading = "Filtering",
430 long_help=r#"Do not sync objects that have tag matching a given regular expression.
431Keys must be sorted in alphabetical order, and '&' separated.
432This filter is applied after all other filters.
433It takes an extra API call to get tags of the object.
434
435Example: "key1=(value1|value2)&key2=value2""#)]
436 filter_exclude_tag_regex: Option<String>,
437
438 #[arg(long, env, conflicts_with_all = ["enable_versioning"], default_value_t = DEFAULT_REMOVE_MODIFIED_FILTER, help_heading = "Update Checking")]
440 remove_modified_filter: bool,
441
442 #[arg(long, env, conflicts_with_all = ["enable_versioning", "check_etag", "check_mtime_and_etag", "check_mtime_and_additional_checksum"], default_value_t = DEFAULT_CHECK_SIZE, help_heading = "Update Checking")]
444 check_size: bool,
445
446 #[arg(long, env, conflicts_with_all = ["enable_versioning", "check_size", "check_mtime_and_etag", "check_mtime_and_additional_checksum", "source_sse_c_key", "target_sse_c_key"], default_value_t = DEFAULT_CHECK_ETAG, help_heading = "Update Checking",)]
448 check_etag: bool,
449
450 #[arg(long, env, conflicts_with_all = ["enable_versioning", "remove_modified_filter", "check_size", "check_etag", "source_sse_c_key", "target_sse_c_key"], default_value_t = DEFAULT_CHECK_MTIME_AND_ETAG, help_heading = "Update Checking",
451 long_help=r#"Use the modification time and ETag for update checking.
452If the source modification date is newer, check the ETag.
453"#)]
454 check_mtime_and_etag: bool,
455
456 #[arg(long, env, conflicts_with_all = ["enable_versioning", "check_size", "check_etag", "check_mtime_and_etag", "check_mtime_and_additional_checksum"], value_parser = checksum_algorithm::parse_checksum_algorithm, help_heading = "Update Checking")]
458 check_additional_checksum: Option<String>,
459
460 #[arg(long, env, conflicts_with_all = ["enable_versioning", "remove_modified_filter", "check_size", "check_etag", "check_mtime_and_etag", "check_additional_checksum"], value_parser = checksum_algorithm::parse_checksum_algorithm, help_heading = "Update Checking",
461 long_help=r#"Use the modification time and additional checksum for update checking.
462If the source modification date is newer, check the additional checksum.
463"#)]
464 check_mtime_and_additional_checksum: Option<String>,
465
466 #[arg(long, env, value_parser = checksum_algorithm::parse_checksum_algorithm, help_heading = "Verification")]
468 additional_checksum_algorithm: Option<String>,
469
470 #[arg(long, env, default_value_t = DEFAULT_FULL_OBJECT_CHECKSUM, help_heading = "Verification", long_help=r#"Use full object checksum for verification. CRC64NVME automatically use full object checksum.
471This option cannot be used with SHA1/SHA256 additional checksum."#)]
472 full_object_checksum: bool,
473
474 #[arg(long, env, default_value_t = DEFAULT_ENABLE_ADDITIONAL_CHECKSUM, help_heading = "Verification")]
476 enable_additional_checksum: bool,
477
478 #[arg(long, env, default_value_t = DEFAULT_DISABLE_MULTIPART_VERIFY, help_heading = "Verification")]
480 disable_multipart_verify: bool,
481
482 #[arg(long, env, default_value_t = DEFAULT_DISABLE_ETAG_VERIFY, help_heading = "Verification")]
484 disable_etag_verify: bool,
485
486 #[arg(long, env, requires = "additional_checksum_algorithm", default_value_t = DEFAULT_DISABLE_ADDITIONAL_CHECKSUM_VERIFY, help_heading = "Verification",
487 long_help=r#"Disable additional checksum verification
488But use additional checksum for upload (The hash value is stored in the target object)."#)]
489 disable_additional_checksum_verify: bool,
490
491 #[arg(long, env, default_value_t = DEFAULT_WORKER_SIZE, value_parser = clap::value_parser!(u16).range(1..), help_heading = "Performance")]
493 worker_size: u16,
494
495 #[arg(long, env, default_value_t = DEFAULT_MAX_PARALLEL_MULTIPART_UPLOADS, value_parser = clap::value_parser!(u16).range(1..), help_heading = "Performance")]
497 max_parallel_uploads: u16,
498
499 #[arg(long, env, default_value_t = DEFAULT_MAX_PARALLEL_LISTINGS, value_parser = clap::value_parser!(u16).range(1..), help_heading = "Performance", long_help=r#"Maximum number of parallel listings of objects."#)]
500 max_parallel_listings: u16,
501
502 #[arg(long, env, default_value_t = DEFAULT_PARALLEL_LISTING_MAX_DEPTH, value_parser = clap::value_parser!(u16).range(1..), help_heading = "Performance", long_help=r#"Maximum depth(sub directroy/prefix) of parallel listings."#)]
503 max_parallel_listing_max_depth: u16,
504
505 #[arg(long, env, default_value_t = DEFAULT_OBJECT_LISTING_QUEUE_SIZE, value_parser = clap::value_parser!(u32).range(1..), help_heading = "Performance")]
507 object_listing_queue_size: u32,
508
509 #[arg(long, env, default_value_t = DEFAULT_ALLOW_PARALLEL_LISTINGS_IN_EXPRESS_ONE_ZONE, help_heading = "Performance", long_help=r#"Allow parallel listings in express one zone storage class.
510It may include multipart upload in progress objects in the listing result."#)]
511 allow_parallel_listings_in_express_one_zone: bool,
512
513 #[arg(long, env, value_parser = clap::value_parser!(u32).range(10..), help_heading = "Performance")]
515 rate_limit_objects: Option<u32>,
516
517 #[arg(long, env, value_parser = human_bytes::check_human_bandwidth, help_heading = "Performance")]
519 rate_limit_bandwidth: Option<String>,
520
521 #[arg(long, env, conflicts_with_all = ["auto_chunksize"], default_value = DEFAULT_MULTIPART_THRESHOLD, value_parser = human_bytes::check_human_bytes, help_heading = "Multipart Settings",
522 long_help=r#"Object size threshold that s3sync uses for multipart upload
523Allow suffixes: MB, MiB, GB, GiB.
524The larger the size, the larger the memory usage."#)]
525 multipart_threshold: String,
526
527 #[arg(long, env, conflicts_with_all = ["auto_chunksize"], default_value = DEFAULT_MULTIPART_CHUNKSIZE, value_parser = human_bytes::check_human_bytes, help_heading = "Multipart Settings",
528 long_help=r#"Chunk size that s3sync uses for multipart upload of individual files
529Allow suffixes: MB, MiB, GB, GiB.
530The larger the size, the larger the memory usage."#)]
531 multipart_chunksize: String,
532
533 #[arg(long, env, conflicts_with_all = ["multipart_threshold", "multipart_chunksize"], default_value_t = DEFAULT_AUTO_CHUNKSIZE, help_heading = "Multipart Settings",
534 long_help=r#"Automatically adjusts a chunk size to match the source or target.
535It takes extra HEAD requests(1 API call per part)."#)]
536 auto_chunksize: bool,
537
538 #[arg(long, env, help_heading = "Metadata/Headers")]
540 cache_control: Option<String>,
541
542 #[arg(long, env, help_heading = "Metadata/Headers")]
544 content_disposition: Option<String>,
545
546 #[arg(long, env, help_heading = "Metadata/Headers")]
548 content_encoding: Option<String>,
549
550 #[arg(long, env, help_heading = "Metadata/Headers")]
552 content_language: Option<String>,
553
554 #[arg(long, env, help_heading = "Metadata/Headers")]
556 content_type: Option<String>,
557
558 #[arg(
559 long,
560 env,
561 help_heading = "Metadata/Headers",
562 long_help = r#"Expires HTTP header to set on the target object(RFC3339 datetime)
563Example: 2023-02-19T12:00:00Z"#
564 )]
565 expires: Option<DateTime<Utc>>,
566
567 #[arg(long, env, value_parser = metadata::check_metadata, help_heading = "Metadata/Headers", long_help=r#"Metadata to set on the target object
568Example: key1=value1,key2=value2"#)]
569 metadata: Option<String>,
570
571 #[arg(long, env, help_heading = "Metadata/Headers")]
573 website_redirect: Option<String>,
574
575 #[arg(long, env, default_value_t = DEFAULT_NO_SYNC_SYSTEM_METADATA, help_heading = "Metadata/Headers",
576 long_help= r#"Do not sync system metadata
577System metadata: content-disposition, content-encoding, content-language, content-type,
578 cache-control, expires, website-redirect"#)]
579 no_sync_system_metadata: bool,
580
581 #[arg(long, env, default_value_t = DEFAULT_NO_SYNC_USER_DEFINED_METADATA, help_heading = "Metadata/Headers")]
583 no_sync_user_defined_metadata: bool,
584
585 #[arg(long, env, conflicts_with_all = ["disable_tagging", "sync_latest_tagging"], value_parser = tagging::parse_tagging, help_heading = "Tagging",
586 long_help=r#"Tagging to set on the target object.
587Key/value must be encoded as UTF-8 then URLEncoded URL query parameters without tag name duplicates.
588
589Example: key1=value1&key2=value2"#)]
590 tagging: Option<String>,
591
592 #[arg(long, env, default_value_t = DEFAULT_DISABLE_TAGGING, help_heading = "Tagging")]
594 disable_tagging: bool,
595
596 #[arg(long, env, conflicts_with_all = ["enable_versioning", "disable_tagging"], default_value_t = DEFAULT_SYNC_LATEST_TAGGING, help_heading = "Tagging",
597 long_help=r#"Copy the latest tagging from the source if necessary.
598If this option is enabled, the --remove-modified-filter and
599--head-each-target options are automatically enabled."#)]
600 sync_latest_tagging: bool,
601
602 #[arg(long, env, default_value_t = DEFAULT_ENABLE_SYNC_OBJECT_ANNOTATION, help_heading = "Object Annotation",
603 long_help=r#"Copy object annotations from the source if necessary.
604If this option is enabled, extra API calls are required."#)]
605 enable_sync_object_annotations: bool,
606
607 #[arg(long, env, default_value_t = DEFAULT_DISABLE_CHECK_ANNOTATION_ETAG, help_heading = "Object Annotation",
608 long_help=r#"Don't use ETag for update annotation checking"#)]
609 disable_check_annotation_etag: bool,
610
611 #[arg(long, env, default_value_t = DEFAULT_SYNC_LATEST_OBJECT_ANNOTATION, conflicts_with_all = ["enable_versioning", "enable_sync_object_annotations"], help_heading = "Object Annotation",
612 long_help=r#"Copy the latest object annotation from the source if necessary.
613If this option is enabled, the --remove-modified-filter and
614--head-each-target options are automatically enabled. And extra API calls are required."#)]
615 sync_latest_object_annotations: bool,
616
617 #[arg(long, env, conflicts_with_all = ["delete", "head_each_target", "remove_modified_filter"], default_value_t = DEFAULT_ENABLE_VERSIONING, help_heading = "Versioning",
618 long_help=r#"Sync all version objects in the source storage to the target versioning storage.
619 "#)]
620 enable_versioning: bool,
621
622 #[arg(
623 long,
624 env,
625 conflicts_with_all = ["delete", "head_each_target", "enable_versioning", "check_etag", "check_mtime_and_etag", "check_size", "check_mtime_and_additional_checksum"],
626 help_heading = "Versioning",
627 long_help = r#"Sync only objects at a specific point in time (RFC3339 datetime).
628The source storage must be a versioning enabled S3 bucket.
629By default, the target storage's objects will always be overwritten with the source's objects.
630
631Example: 2025-07-16T12:00:00Z"#
632 )]
633 point_in_time: Option<DateTime<Utc>>,
634
635 #[arg(long, env, value_parser = sse::parse_sse, help_heading = "Encryption")]
637 sse: Option<String>,
638
639 #[arg(long, env, help_heading = "Encryption")]
641 sse_kms_key_id: Option<String>,
642
643 #[arg(long, env, conflicts_with_all = ["sse", "sse_kms_key_id"], requires = "source_sse_c_key", value_parser = sse::parse_sse_c, help_heading = "Encryption")]
645 source_sse_c: Option<String>,
646
647 #[arg(
649 long,
650 env,
651 hide_env_values = true,
652 requires = "source_sse_c_key_md5",
653 help_heading = "Encryption"
654 )]
655 source_sse_c_key: Option<String>,
656
657 #[arg(
659 long,
660 env,
661 hide_env_values = true,
662 requires = "source_sse_c",
663 help_heading = "Encryption"
664 )]
665 source_sse_c_key_md5: Option<String>,
666
667 #[arg(long, env, conflicts_with_all = ["sse", "sse_kms_key_id"], requires = "target_sse_c_key", value_parser = sse::parse_sse_c, help_heading = "Encryption")]
669 target_sse_c: Option<String>,
670
671 #[arg(
673 long,
674 env,
675 hide_env_values = true,
676 requires = "target_sse_c_key_md5",
677 help_heading = "Encryption"
678 )]
679 target_sse_c_key: Option<String>,
680
681 #[arg(
683 long,
684 env,
685 hide_env_values = true,
686 requires = "target_sse_c",
687 help_heading = "Encryption"
688 )]
689 target_sse_c_key_md5: Option<String>,
690
691 #[clap(flatten)]
693 verbosity: Verbosity<WarnLevel>,
694
695 #[arg(
696 long,
697 env,
698 default_value_t = DEFAULT_REPORT_SYNC_STATUS,
699 conflicts_with_all = ["dry_run", "delete", "enable_versioning", "head_each_target", "point_in_time"],
700 help_heading = "Reporting",
701 long_help = r#"Report sync status to the target storage.
702Default verification is for ETag. For additional checksum, use --check-additional-checksum.
703For more precise control, use with --auto-chunksize."#
704 )]
705 report_sync_status: bool,
706
707 #[arg(
708 long,
709 env,
710 default_value_t = DEFAULT_REPORT_METADATA_SYNC_STATUS,
711 requires = "report_sync_status",
712 help_heading = "Reporting",
713 long_help = r#"Report metadata sync status to the target storage.
714It must be used with --report-sync-status.
715Note: s3sync generated user-defined metadata(s3sync_origin_version_id/s3sync_origin_last_modified) were ignored.
716 Because they are usually different from the source storage."#
717 )]
718 report_metadata_sync_status: bool,
719
720 #[arg(
721 long,
722 env,
723 default_value_t = DEFAULT_REPORT_TAGGING_SYNC_STATUS,
724 requires = "report_sync_status",
725 help_heading = "Reporting",
726 long_help = r#"Report tagging sync status to the target storage.
727It must be used with --report-sync-status."#
728 )]
729 report_tagging_sync_status: bool,
730
731 #[arg(
732 long,
733 env,
734 default_value_t = DEFAULT_REPORT_ANNOTATIONS_SYNC_STATUS,
735 requires = "report_sync_status",
736 help_heading = "Reporting",
737 long_help = r#"Report annotations sync status to the target storage.
738It must be used with --report-sync-status."#
739 )]
740 report_annotations_sync_status: bool,
741
742 #[arg(long, env, default_value_t = DEFAULT_JSON_TRACING, help_heading = "Tracing/Logging")]
744 json_tracing: bool,
745
746 #[arg(long, env, default_value_t = DEFAULT_AWS_SDK_TRACING, help_heading = "Tracing/Logging")]
748 aws_sdk_tracing: bool,
749
750 #[arg(long, env, default_value_t = DEFAULT_SPAN_EVENTS_TRACING, help_heading = "Tracing/Logging")]
752 span_events_tracing: bool,
753
754 #[arg(long, env, default_value_t = DEFAULT_DISABLE_COLOR_TRACING, help_heading = "Tracing/Logging")]
756 disable_color_tracing: bool,
757
758 #[arg(
759 long,
760 env,
761 default_value_t = DEFAULT_TRACING_STDERR,
762 help_heading = "Tracing/Logging",
763 long_help = r#"Output all tracing to stderr. By default, tracing messages are written to stdout."#
764 )]
765 tracing_stderr: bool,
766
767 #[arg(long, env, default_value_t = DEFAULT_AWS_MAX_ATTEMPTS, value_name = "max_attempts", help_heading = "Retry Options")]
769 aws_max_attempts: u32,
770
771 #[arg(long, env, default_value_t = DEFAULT_INITIAL_BACKOFF_MILLISECONDS, value_name = "initial_backoff", help_heading = "Retry Options",
772 long_help=r#"A multiplier value used when calculating backoff times as part of an exponential backoff with jitter strategy.
773"#)]
774 initial_backoff_milliseconds: u64,
775
776 #[arg(long, env, default_value_t = DEFAULT_FORCE_RETRY_COUNT, help_heading = "Retry Options")]
778 force_retry_count: u32,
779
780 #[arg(long, env, default_value_t = DEFAULT_FORCE_RETRY_INTERVAL_MILLISECONDS, value_name = "force_retry_interval", help_heading = "Retry Options",
781 long_help=r#"Sleep interval (milliseconds) between s3sync force retries on error.
782"#)]
783 force_retry_interval_milliseconds: u64,
784
785 #[arg(
786 long,
787 env,
788 value_name = "operation_timeout",
789 help_heading = "Timeout Options",
790 long_help = r#"Operation timeout (milliseconds). For details, see the AWS SDK for Rust TimeoutConfig documentation.
791The default has no timeout."#
792 )]
793 operation_timeout_milliseconds: Option<u64>,
794
795 #[arg(
796 long,
797 env,
798 value_name = "operation_attempt_timeout",
799 help_heading = "Timeout Options",
800 long_help = r#"Operation attempt timeout (milliseconds). For details, see the AWS SDK for Rust TimeoutConfig documentation.
801The default has no timeout."#
802 )]
803 operation_attempt_timeout_milliseconds: Option<u64>,
804
805 #[arg(
806 long,
807 env,
808 value_name = "connect_timeout",
809 help_heading = "Timeout Options",
810 long_help = r#"Connect timeout (milliseconds).The default has AWS SDK default timeout (Currently 3100 milliseconds).
811"#
812 )]
813 connect_timeout_milliseconds: Option<u64>,
814
815 #[arg(
816 long,
817 env,
818 value_name = "read_timeout",
819 help_heading = "Timeout Options",
820 long_help = r#"Read timeout (milliseconds). The default has no timeout."#
821 )]
822 read_timeout_milliseconds: Option<u64>,
823
824 #[arg(long, env, default_value_t = DEFAULT_WARN_AS_ERROR, help_heading = "Advanced")]
826 warn_as_error: bool,
827
828 #[arg(long, env, default_value_t = DEFAULT_IGNORE_SYMLINKS, help_heading = "Advanced")]
830 ignore_symlinks: bool,
831
832 #[arg(long, env, conflicts_with_all = ["enable_versioning"], default_value_t = DEFAULT_HEAD_EACH_TARGET, help_heading = "Advanced",
833 long_help=r#"HeadObject is used to check whether an object has been modified in the target storage.
834It reduces the possibility of race condition issue"#)]
835 head_each_target: bool,
836
837 #[arg(long, env, value_parser = canned_acl::parse_canned_acl, help_heading = "Advanced",
838 long_help=r#"ACL for the objects
839Valid choices: private | public-read | public-read-write | authenticated-read | aws-exec-read |
840 bucket-owner-read | bucket-owner-full-control"#)]
841 acl: Option<String>,
842
843 #[arg(long, env, default_value_t = DEFAULT_NO_GUESS_MIME_TYPE, help_heading = "Advanced")]
845 no_guess_mime_type: bool,
846
847 #[arg(long, env, default_value_t = DEFAULT_MAX_KEYS, value_parser = clap::value_parser!(i32).range(1..=32767), help_heading = "Advanced")]
849 max_keys: i32,
850
851 #[arg(long, env, default_value_t = DEFAULT_PUT_LAST_MODIFIED_METADATA, help_heading = "Advanced")]
853 put_last_modified_metadata: bool,
854
855 #[arg(long, env, value_name = "SHELL", value_parser = clap_complete::shells::Shell::from_str, help_heading = "Advanced",
856 long_help=r#"Generate a auto completions script.
857Valid choices: bash, fish, zsh, powershell, elvish."#)]
858 auto_complete_shell: Option<clap_complete::shells::Shell>,
859
860 #[arg(long, env, default_value_t = DEFAULT_DISABLE_STALLED_STREAM_PROTECTION, help_heading = "Advanced")]
862 disable_stalled_stream_protection: bool,
863
864 #[arg(long, env, default_value_t = DEFAULT_DISABLE_PAYLOAD_SIGNING, help_heading = "Advanced")]
866 disable_payload_signing: bool,
867
868 #[arg(long, env, default_value_t = DEFAULT_DISABLE_CONTENT_MD5_HEADER, help_heading = "Advanced",
869 long_help=r#"Disable Content-MD5 header for object uploads. It disables the ETag verification for the uploaded object.
870"#)]
871 disable_content_md5_header: bool,
872
873 #[arg(long, env, default_value_t = DEFAULT_DISABLE_EXPRESS_ONE_ZONE_ADDITIONAL_CHECKSUM, help_heading = "Advanced",
874 long_help=r#"Disable default additional checksum verification in Express One Zone storage class.
875 "#)]
876 disable_express_one_zone_additional_checksum: bool,
877
878 #[arg(long, env, requires = "delete", default_value_t = DEFAULT_DELETE_EXCLUDED, help_heading = "Advanced",
879 long_help=r#"When used in combination with --delete options, supplied --filter-exclude-regex patterns will not prevent an object from being deleted.
880"#)]
881 delete_excluded: bool,
882
883 #[arg(long, env, conflicts_with_all = ["enable_versioning", "point_in_time"], default_value_t = DEFAULT_IF_MATCH, help_heading = "Advanced", long_help=r#"Add an If-Match header for PutObject/CompleteMultipartUpload/DeleteObject requests.
884This is for like an optimistic lock."#)]
885 if_match: bool,
886
887 #[arg(long, env, conflicts_with_all = ["enable_versioning", "point_in_time"], requires = "server_side_copy", default_value_t = DEFAULT_COPY_SOURCE_IF_MATCH, help_heading = "Advanced", long_help=r#"Add an x-amz-copy-source-if-match header for CopyObject/UploadPartCopy requests.
888This is for like an optimistic lock."#)]
889 copy_source_if_match: bool,
890
891 #[arg(long, env, conflicts_with_all = ["enable_versioning", "point_in_time", "if_match"], default_value_t = DEFAULT_IF_NONE_MATCH, help_heading = "Advanced", long_help=r#"Uploads the object only if the object key name does not already exist in the specified bucket.
892This is for like an optimistic lock."#)]
893 if_none_match: bool,
894
895 #[arg(long, env, requires = "delete", value_parser = clap::value_parser!(u64).range(1..), help_heading = "Advanced")]
897 max_delete: Option<u64>,
898
899 #[arg(long, env, default_value_t = DEFAULT_IGNORE_GLACIER_WARNINGS, help_heading = "Advanced")]
901 ignore_glacier_warnings: bool,
902
903 #[cfg(feature = "lua_support")]
904 #[arg(
905 long,
906 env,
907 help_heading = "Lua scripting support",
908 value_parser = file_exist::is_file_exist,
909 long_help = r#"Path to the Lua script that is executed as preprocess callback"#
910 )]
911 preprocess_callback_lua_script: Option<String>,
912
913 #[cfg(feature = "lua_support")]
914 #[arg(
915 long,
916 env,
917 help_heading = "Lua scripting support",
918 value_parser = file_exist::is_file_exist,
919 long_help = r#"Path to the Lua script that is executed as event callback"#
920 )]
921 event_callback_lua_script: Option<String>,
922
923 #[cfg(feature = "lua_support")]
924 #[arg(
925 long,
926 env,
927 help_heading = "Lua scripting support",
928 value_parser = file_exist::is_file_exist,
929 long_help = r#"Path to the Lua script that is executed as filter callback"#
930 )]
931 filter_callback_lua_script: Option<String>,
932
933 #[cfg(feature = "lua_support")]
934 #[arg(long, env, conflicts_with_all = ["allow_lua_unsafe_vm"], default_value_t = DEFAULT_ALLOW_LUA_OS_LIBRARY, help_heading = "Lua scripting support", long_help="Allow Lua OS and I/O library functions in the Lua script.")]
935 allow_lua_os_library: bool,
936
937 #[cfg(feature = "lua_support")]
938 #[arg(long, env, default_value = DEFAULT_LUA_VM_MEMORY_LIMIT, value_parser = human_bytes::check_human_bytes_without_limit, help_heading = "Lua scripting support",
939 long_help=r#"Memory limit for the Lua VM. Allow suffixes: KB, KiB, MB, MiB, GB, GiB.
940Zero means no limit.
941If the memory limit is exceeded, the whole process will be terminated."#)]
942 lua_vm_memory_limit: String,
943
944 #[cfg(feature = "lua_support")]
945 #[arg(long, env, default_value = DEFAULT_LUA_CALLBACK_TIMEOUT_MILLISECONDS, value_parser = clap::value_parser!(u64), help_heading = "Lua scripting support",
946 long_help=r#"Timeout in milliseconds for Lua callback execution.
947Zero means no timeout.
948If the timeout is exceeded, the callback will be terminated with an error."#)]
949 lua_callback_timeout_milliseconds: u64,
950
951 #[cfg(feature = "lua_support")]
952 #[arg(long, env, conflicts_with_all = ["allow_lua_os_library"], default_value_t = DEFAULT_ALLOW_LUA_UNSAFE_VM, help_heading = "Dangerous",
953 long_help=r#"Allow unsafe Lua VM functions in the Lua script.
954It allows the Lua script to load unsafe standard libraries or C modules."#)]
955 allow_lua_unsafe_vm: bool,
956
957 #[arg(long, env, conflicts_with_all = ["enable_versioning", "sync_latest_tagging", "sync_latest_object_annotations"], default_value_t = DEFAULT_SYNC_WITH_DELETE, help_heading = "Dangerous",
958 long_help=r#"Delete objects that exist in the target but not in the source.
959Exclude filters other than --filter-exclude-regex will not prevent an object from being deleted.
960[Warning] Since this can cause data loss, test first with the --dry-run option.
961 "#)]
962 delete: bool,
963
964 #[arg(long, hide = true, default_value_t = false, help_heading = "Dangerous")]
966 allow_both_local_storage: bool,
967
968 #[cfg(e2e_test_dangerous_simulations)]
970 #[arg(long, hide = true, default_value_t = false, help_heading = "Dangerous")]
971 test_user_defined_callback: bool,
972
973 #[cfg(e2e_test_dangerous_simulations)]
975 #[arg(long, hide = true, default_value_t = false, help_heading = "Dangerous")]
976 allow_e2e_test_dangerous_simulation: bool,
977
978 #[cfg(e2e_test_dangerous_simulations)]
980 #[arg(long, hide = true, help_heading = "Dangerous")]
981 cancellation_point: Option<String>,
982
983 #[cfg(e2e_test_dangerous_simulations)]
985 #[arg(long, hide = true, help_heading = "Dangerous")]
986 panic_simulation_point: Option<String>,
987
988 #[cfg(e2e_test_dangerous_simulations)]
990 #[arg(long, hide = true, help_heading = "Dangerous")]
991 error_simulation_point: Option<String>,
992}
993
994pub fn parse_from_args<I, T>(args: I) -> Result<CLIArgs, clap::Error>
995where
996 I: IntoIterator<Item = T>,
997 T: Into<OsString> + Clone,
998{
999 CLIArgs::try_parse_from(args)
1000}
1001
1002pub fn build_config_from_args<I, T>(args: I) -> Result<Config, String>
1003where
1004 I: IntoIterator<Item = T>,
1005 T: Into<OsString> + Clone,
1006{
1007 let config_args = CLIArgs::try_parse_from(args).map_err(|e| e.to_string())?;
1008 crate::Config::try_from(config_args)
1009}
1010
1011impl CLIArgs {
1012 fn validate_storage_config(&self) -> Result<(), String> {
1014 self.check_source_local_storage()?;
1015 self.check_target_local_storage()?;
1016 self.check_storage_conflict()?;
1017 self.check_versioning_option_conflict()?;
1018 self.check_tagging_option_conflict()?;
1019 self.check_storage_class_conflict()?;
1020 self.check_storage_credentials_conflict()?;
1021 self.check_sse_conflict()?;
1022 self.check_sse_c_conflict()?;
1023 self.check_acl_conflict()?;
1024 self.check_enable_additional_checksum_conflict()?;
1025 self.check_additional_checksum_algorithm_conflict()?;
1026 self.check_auto_chunksize_conflict()?;
1027 self.check_metadata_conflict()?;
1028 self.check_check_size_conflict()?;
1029 self.check_check_e_tag_conflict()?;
1030 self.check_check_mtime_and_e_tag_conflict()?;
1031 self.check_ignore_symlinks_conflict()?;
1032 self.check_no_guess_mime_type_conflict()?;
1033 self.check_endpoint_url_conflict()?;
1034 self.check_no_sign_request_conflict()?;
1035 self.check_disable_payload_signing_conflict()?;
1036 self.check_disable_content_md5_header_conflict()?;
1037 self.check_full_object_checksum_conflict()?;
1038 self.check_accelerate_conflict()?;
1039 self.check_request_payer_conflict()?;
1040 self.check_server_side_copy_conflict()?;
1041 self.check_filter_include_metadata_regex_conflict()?;
1042 self.check_filter_exclude_metadata_regex_conflict()?;
1043 self.check_filter_include_tag_regex_conflict()?;
1044 self.check_filter_exclude_tag_regex_conflict()?;
1045 self.check_server_no_sync_system_metadata_conflict()?;
1046 self.check_server_no_sync_user_defined_metadata_conflict()?;
1047 self.check_point_in_time_conflict()?;
1048 self.check_report_metadata_sync_status_conflict()?;
1049 self.check_report_tagging_sync_status_conflict()?;
1050 self.check_report_annotations_sync_status_conflict()?;
1051 self.check_if_match_conflict()?;
1052 self.check_if_none_match_conflict()?;
1053 self.check_copy_source_if_match_conflict()?;
1054 self.check_enable_sync_object_annotation_conflict()?;
1055 self.check_sync_latest_object_annotation_conflict()?;
1056
1057 Ok(())
1058 }
1059
1060 fn check_source_local_storage(&self) -> Result<(), String> {
1061 let source = storage_path::parse_storage_path(&self.source);
1062
1063 if let StoragePath::Local(path) = source {
1064 if !path.is_dir() && self.delete {
1065 return Err(SOURCE_LOCAL_STORAGE_FILE_WITH_DELETE_OPTION.to_string());
1066 }
1067 if !path.exists() {
1068 return Err(SOURCE_LOCAL_STORAGE_PATH_NOT_FOUND.to_string());
1069 }
1070 }
1071
1072 Ok(())
1073 }
1074
1075 fn check_target_local_storage(&self) -> Result<(), String> {
1076 let target = storage_path::parse_storage_path(&self.target);
1077
1078 if let StoragePath::Local(path) = target {
1079 let exist_result = path.try_exists();
1080 if exist_result.is_err() {
1081 return Err(TARGET_LOCAL_STORAGE_INVALID.to_string());
1082 }
1083 }
1084 Ok(())
1085 }
1086
1087 fn check_storage_conflict(&self) -> Result<(), String> {
1088 if self.allow_both_local_storage {
1089 return Ok(());
1090 }
1091
1092 let source = storage_path::parse_storage_path(&self.source);
1093 let target = storage_path::parse_storage_path(&self.target);
1094
1095 if storage_path::is_both_storage_local(&source, &target) {
1096 return Err(NO_S3_STORAGE_SPECIFIED.to_string());
1097 }
1098
1099 Ok(())
1100 }
1101
1102 fn check_versioning_option_conflict(&self) -> Result<(), String> {
1103 if !self.enable_versioning {
1104 return Ok(());
1105 }
1106
1107 let source = storage_path::parse_storage_path(&self.source);
1108 let target = storage_path::parse_storage_path(&self.target);
1109
1110 if !storage_path::is_both_storage_s3(&source, &target) {
1111 return Err(LOCAL_STORAGE_SPECIFIED.to_string());
1112 }
1113
1114 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.source) {
1115 if is_express_onezone_storage(&bucket) {
1116 return Err(VERSIONING_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE.to_string());
1117 }
1118 }
1119
1120 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.target) {
1121 if is_express_onezone_storage(&bucket) {
1122 return Err(VERSIONING_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE.to_string());
1123 }
1124 }
1125
1126 Ok(())
1127 }
1128
1129 fn check_tagging_option_conflict(&self) -> Result<(), String> {
1130 let source = storage_path::parse_storage_path(&self.source);
1131 let target = storage_path::parse_storage_path(&self.target);
1132
1133 if self.sync_latest_tagging && !storage_path::is_both_storage_s3(&source, &target) {
1134 return Err(LOCAL_STORAGE_SPECIFIED.to_string());
1135 }
1136
1137 Ok(())
1138 }
1139
1140 fn check_storage_class_conflict(&self) -> Result<(), String> {
1141 let target = storage_path::parse_storage_path(&self.target);
1142
1143 if self.storage_class.is_some() && matches!(target, StoragePath::Local(_)) {
1144 return Err(LOCAL_STORAGE_SPECIFIED_WITH_STORAGE_CLASS.to_string());
1145 }
1146
1147 Ok(())
1148 }
1149
1150 fn check_storage_credentials_conflict(&self) -> Result<(), String> {
1151 let source = storage_path::parse_storage_path(&self.source);
1152 let target = storage_path::parse_storage_path(&self.target);
1153
1154 if matches!(source, StoragePath::Local(_))
1155 && (self.source_profile.is_some() || self.source_access_key.is_some())
1156 {
1157 return Err(NO_SOURCE_CREDENTIAL_REQUIRED.to_string());
1158 }
1159
1160 if matches!(target, StoragePath::Local(_))
1161 && (self.target_profile.is_some() || self.target_access_key.is_some())
1162 {
1163 return Err(NO_TARGET_CREDENTIAL_REQUIRED.to_string());
1164 }
1165
1166 Ok(())
1167 }
1168
1169 fn check_sse_conflict(&self) -> Result<(), String> {
1170 if self.sse.is_none() && self.sse_kms_key_id.is_none() {
1171 return Ok(());
1172 }
1173
1174 let target = storage_path::parse_storage_path(&self.target);
1175 if matches!(target, StoragePath::Local(_)) {
1176 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_SSE.to_string());
1177 }
1178
1179 if self.sse_kms_key_id.is_some()
1180 && (self.sse.is_none()
1181 || (ServerSideEncryption::from_str(self.sse.as_ref().unwrap()).unwrap()
1182 != ServerSideEncryption::AwsKms
1183 && ServerSideEncryption::from_str(self.sse.as_ref().unwrap()).unwrap()
1184 != ServerSideEncryption::AwsKmsDsse))
1185 {
1186 return Err(SSE_KMS_KEY_ID_ARGUMENTS_CONFLICT.to_string());
1187 }
1188
1189 Ok(())
1190 }
1191
1192 fn check_sse_c_conflict(&self) -> Result<(), String> {
1193 if self.source_sse_c.is_none() && self.target_sse_c.is_none() {
1194 return Ok(());
1195 }
1196
1197 if self.source_sse_c.is_some() {
1198 let source = storage_path::parse_storage_path(&self.source);
1199 if matches!(source, StoragePath::Local(_)) {
1200 return Err(LOCAL_STORAGE_SPECIFIED_WITH_SSE_C.to_string());
1201 }
1202 }
1203
1204 if self.target_sse_c.is_some() {
1205 let target = storage_path::parse_storage_path(&self.target);
1206 if matches!(target, StoragePath::Local(_)) {
1207 return Err(LOCAL_STORAGE_SPECIFIED_WITH_SSE_C.to_string());
1208 }
1209 }
1210
1211 Ok(())
1212 }
1213
1214 fn check_acl_conflict(&self) -> Result<(), String> {
1215 if self.acl.is_none() {
1216 return Ok(());
1217 }
1218
1219 let target = storage_path::parse_storage_path(&self.target);
1220 if matches!(target, StoragePath::Local(_)) {
1221 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_ACL.to_string());
1222 }
1223
1224 Ok(())
1225 }
1226
1227 fn check_additional_checksum_algorithm_conflict(&self) -> Result<(), String> {
1228 if self.additional_checksum_algorithm.is_none() {
1229 return Ok(());
1230 }
1231
1232 let target = storage_path::parse_storage_path(&self.target);
1233 if matches!(target, StoragePath::Local(_)) {
1234 return Err(
1235 TARGET_LOCAL_STORAGE_SPECIFIED_WITH_ADDITIONAL_CHECKSUM_ALGORITHM.to_string(),
1236 );
1237 }
1238
1239 Ok(())
1240 }
1241
1242 fn check_enable_additional_checksum_conflict(&self) -> Result<(), String> {
1243 if !self.enable_additional_checksum {
1244 return Ok(());
1245 }
1246
1247 let source = storage_path::parse_storage_path(&self.source);
1248 if matches!(source, StoragePath::Local(_)) {
1249 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_ENABLE_ADDITIONAL_CHECKSUM.to_string());
1250 }
1251
1252 Ok(())
1253 }
1254
1255 fn check_auto_chunksize_conflict(&self) -> Result<(), String> {
1256 if !self.auto_chunksize {
1257 return Ok(());
1258 }
1259
1260 let source = storage_path::parse_storage_path(&self.source);
1261
1262 if !self.check_etag && !self.check_mtime_and_etag && matches!(source, StoragePath::Local(_))
1263 {
1264 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_AUTO_CHUNKSIZE.to_string());
1265 }
1266
1267 Ok(())
1268 }
1269
1270 fn check_metadata_conflict(&self) -> Result<(), String> {
1271 if self.cache_control.is_none()
1272 && self.content_disposition.is_none()
1273 && self.content_encoding.is_none()
1274 && self.content_language.is_none()
1275 && self.content_type.is_none()
1276 && self.website_redirect.is_none()
1277 && self.expires.is_none()
1278 && self.tagging.is_none()
1279 && !self.put_last_modified_metadata
1280 {
1281 return Ok(());
1282 }
1283
1284 let target = storage_path::parse_storage_path(&self.target);
1285 if matches!(target, StoragePath::Local(_)) {
1286 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_METADATA_OPTION.to_string());
1287 }
1288
1289 Ok(())
1290 }
1291
1292 fn check_check_size_conflict(&self) -> Result<(), String> {
1293 if self.check_size && self.remove_modified_filter && !self.head_each_target {
1294 return Err(CHECK_SIZE_CONFLICT.to_string());
1295 }
1296
1297 Ok(())
1298 }
1299
1300 fn check_check_e_tag_conflict(&self) -> Result<(), String> {
1301 if !self.check_etag {
1302 return Ok(());
1303 }
1304 if self.remove_modified_filter && !self.head_each_target {
1305 return Err(CHECK_ETAG_CONFLICT.to_string());
1306 }
1307
1308 if self.sse.is_some() {
1309 let sse = ServerSideEncryption::from_str(self.sse.as_ref().unwrap()).unwrap();
1310 if sse == ServerSideEncryption::AwsKms || sse == ServerSideEncryption::AwsKmsDsse {
1311 return Err(CHECK_ETAG_CONFLICT_SSE_KMS.to_string());
1312 }
1313 }
1314
1315 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.source) {
1316 if is_express_onezone_storage(&bucket) {
1317 return Err(CHECK_ETAG_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE.to_string());
1318 }
1319 }
1320
1321 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.target) {
1322 if is_express_onezone_storage(&bucket) {
1323 return Err(CHECK_ETAG_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE.to_string());
1324 }
1325 }
1326
1327 Ok(())
1328 }
1329
1330 fn check_check_mtime_and_e_tag_conflict(&self) -> Result<(), String> {
1331 if !self.check_mtime_and_etag {
1332 return Ok(());
1333 }
1334
1335 if self.sse.is_some() {
1336 let sse = ServerSideEncryption::from_str(self.sse.as_ref().unwrap()).unwrap();
1337 if sse == ServerSideEncryption::AwsKms || sse == ServerSideEncryption::AwsKmsDsse {
1338 return Err(CHECK_MTIME_AND_ETAG_CONFLICT_SSE_KMS.to_string());
1339 }
1340 }
1341
1342 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.source) {
1343 if is_express_onezone_storage(&bucket) {
1344 return Err(CHECK_MTIME_AND_ETAG_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE.to_string());
1345 }
1346 }
1347
1348 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.target) {
1349 if is_express_onezone_storage(&bucket) {
1350 return Err(CHECK_MTIME_AND_ETAG_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE.to_string());
1351 }
1352 }
1353
1354 Ok(())
1355 }
1356
1357 fn check_ignore_symlinks_conflict(&self) -> Result<(), String> {
1358 if !self.ignore_symlinks {
1359 return Ok(());
1360 }
1361
1362 let source = storage_path::parse_storage_path(&self.source);
1363 if matches!(source, StoragePath::S3 { .. }) {
1364 return Err(SOURCE_REMOTE_STORAGE_SPECIFIED_WITH_IGNORE_SYMLINKS.to_string());
1365 }
1366
1367 Ok(())
1368 }
1369
1370 fn check_no_guess_mime_type_conflict(&self) -> Result<(), String> {
1371 if !self.no_guess_mime_type {
1372 return Ok(());
1373 }
1374
1375 let source = storage_path::parse_storage_path(&self.source);
1376 if matches!(source, StoragePath::S3 { .. }) {
1377 return Err(SOURCE_REMOTE_STORAGE_SPECIFIED_WITH_NO_GUESS_MIME_TYPE.to_string());
1378 }
1379
1380 Ok(())
1381 }
1382
1383 fn check_endpoint_url_conflict(&self) -> Result<(), String> {
1384 let source = storage_path::parse_storage_path(&self.source);
1385 if matches!(source, StoragePath::Local(_)) && self.source_endpoint_url.is_some() {
1386 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_ENDPOINT_URL.to_string());
1387 }
1388
1389 let target = storage_path::parse_storage_path(&self.target);
1390 if matches!(target, StoragePath::Local(_)) && self.target_endpoint_url.is_some() {
1391 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_ENDPOINT_URL.to_string());
1392 }
1393
1394 Ok(())
1395 }
1396
1397 fn check_no_sign_request_conflict(&self) -> Result<(), String> {
1398 if !self.source_no_sign_request {
1399 return Ok(());
1400 }
1401
1402 let source = storage_path::parse_storage_path(&self.source);
1403 if matches!(source, StoragePath::Local(_)) {
1404 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_NO_SIGN_REQUEST.to_string());
1405 }
1406
1407 Ok(())
1408 }
1409
1410 fn check_disable_payload_signing_conflict(&self) -> Result<(), String> {
1411 if !self.disable_payload_signing {
1412 return Ok(());
1413 }
1414
1415 let target = storage_path::parse_storage_path(&self.target);
1416 if matches!(target, StoragePath::Local(_)) {
1417 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_DISABLE_PAYLOAD_SIGNING.to_string());
1418 }
1419
1420 Ok(())
1421 }
1422
1423 fn check_disable_content_md5_header_conflict(&self) -> Result<(), String> {
1424 if !self.disable_content_md5_header {
1425 return Ok(());
1426 }
1427
1428 let target = storage_path::parse_storage_path(&self.target);
1429 if matches!(target, StoragePath::Local(_)) {
1430 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_DISABLE_CONTENT_MD5_HEADER.to_string());
1431 }
1432
1433 Ok(())
1434 }
1435
1436 fn check_full_object_checksum_conflict(&self) -> Result<(), String> {
1437 if !self.full_object_checksum {
1438 return Ok(());
1439 }
1440
1441 let target = storage_path::parse_storage_path(&self.target);
1442 if matches!(target, StoragePath::Local(_)) {
1443 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_FULL_OBJECT_CHECKSUM.to_string());
1444 }
1445
1446 if let Some(additional_checksum_algorithm) = &self.additional_checksum_algorithm {
1447 if additional_checksum_algorithm == "SHA1" || additional_checksum_algorithm == "SHA256"
1448 {
1449 return Err(FULL_OBJECT_CHECKSUM_NOT_SUPPORTED.to_string());
1450 }
1451 }
1452
1453 Ok(())
1454 }
1455
1456 fn check_accelerate_conflict(&self) -> Result<(), String> {
1457 let source = storage_path::parse_storage_path(&self.source);
1458 if matches!(source, StoragePath::Local(_)) && self.source_accelerate {
1459 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_ACCELERATE.to_string());
1460 }
1461
1462 let target = storage_path::parse_storage_path(&self.target);
1463 if matches!(target, StoragePath::Local(_)) && self.target_accelerate {
1464 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_ACCELERATE.to_string());
1465 }
1466
1467 Ok(())
1468 }
1469
1470 fn check_request_payer_conflict(&self) -> Result<(), String> {
1471 let source = storage_path::parse_storage_path(&self.source);
1472 if matches!(source, StoragePath::Local(_)) && self.source_request_payer {
1473 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_REQUEST_PAYER.to_string());
1474 }
1475
1476 let target = storage_path::parse_storage_path(&self.target);
1477 if matches!(target, StoragePath::Local(_)) && self.target_request_payer {
1478 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_REQUEST_PAYER.to_string());
1479 }
1480
1481 Ok(())
1482 }
1483
1484 fn check_server_side_copy_conflict(&self) -> Result<(), String> {
1485 let source = storage_path::parse_storage_path(&self.source);
1486 if matches!(source, StoragePath::Local(_)) && self.server_side_copy {
1487 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_SERVER_SIDE_COPY.to_string());
1488 }
1489
1490 let target = storage_path::parse_storage_path(&self.target);
1491 if matches!(target, StoragePath::Local(_)) && self.server_side_copy {
1492 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_SERVER_SIDE_COPY.to_string());
1493 }
1494
1495 Ok(())
1496 }
1497
1498 fn check_filter_include_metadata_regex_conflict(&self) -> Result<(), String> {
1499 let source = storage_path::parse_storage_path(&self.source);
1500 if matches!(source, StoragePath::Local(_)) && self.filter_include_metadata_regex.is_some() {
1501 return Err(
1502 SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_FILTER_INCLUDE_METADATA_REGEX.to_string(),
1503 );
1504 }
1505
1506 Ok(())
1507 }
1508
1509 fn check_filter_exclude_metadata_regex_conflict(&self) -> Result<(), String> {
1510 let source = storage_path::parse_storage_path(&self.source);
1511 if matches!(source, StoragePath::Local(_)) && self.filter_exclude_metadata_regex.is_some() {
1512 return Err(
1513 SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_FILTER_EXCLUDE_METADATA_REGEX.to_string(),
1514 );
1515 }
1516
1517 Ok(())
1518 }
1519
1520 fn check_filter_include_tag_regex_conflict(&self) -> Result<(), String> {
1521 let source = storage_path::parse_storage_path(&self.source);
1522 if matches!(source, StoragePath::Local(_)) && self.filter_include_tag_regex.is_some() {
1523 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_FILTER_INCLUDE_TAG_REGEX.to_string());
1524 }
1525
1526 Ok(())
1527 }
1528
1529 fn check_filter_exclude_tag_regex_conflict(&self) -> Result<(), String> {
1530 let source = storage_path::parse_storage_path(&self.source);
1531 if matches!(source, StoragePath::Local(_)) && self.filter_exclude_tag_regex.is_some() {
1532 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_FILTER_EXCLUDE_TAG_REGEX.to_string());
1533 }
1534
1535 Ok(())
1536 }
1537
1538 fn check_server_no_sync_system_metadata_conflict(&self) -> Result<(), String> {
1539 let source = storage_path::parse_storage_path(&self.source);
1540 if matches!(source, StoragePath::Local(_)) && self.no_sync_system_metadata {
1541 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_NO_SYNC_SYSTEM_METADATA.to_string());
1542 }
1543
1544 let target = storage_path::parse_storage_path(&self.target);
1545 if matches!(target, StoragePath::Local(_)) && self.no_sync_system_metadata {
1546 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_NO_SYNC_SYSTEM_METADATA.to_string());
1547 }
1548
1549 Ok(())
1550 }
1551
1552 fn check_server_no_sync_user_defined_metadata_conflict(&self) -> Result<(), String> {
1553 let source = storage_path::parse_storage_path(&self.source);
1554 if matches!(source, StoragePath::Local(_)) && self.no_sync_user_defined_metadata {
1555 return Err(
1556 SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_NO_SYNC_USER_DEFINED_METADATA.to_string(),
1557 );
1558 }
1559
1560 let target = storage_path::parse_storage_path(&self.target);
1561 if matches!(target, StoragePath::Local(_)) && self.no_sync_user_defined_metadata {
1562 return Err(
1563 TARGET_LOCAL_STORAGE_SPECIFIED_WITH_NO_SYNC_USER_DEFINED_METADATA.to_string(),
1564 );
1565 }
1566
1567 Ok(())
1568 }
1569 fn check_point_in_time_conflict(&self) -> Result<(), String> {
1570 if self.point_in_time.is_none() {
1571 return Ok(());
1572 }
1573
1574 let source = storage_path::parse_storage_path(&self.source);
1575 if matches!(source, StoragePath::Local(_)) {
1576 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_POINT_IN_TIME.to_string());
1577 }
1578
1579 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.source) {
1580 if is_express_onezone_storage(&bucket) {
1581 return Err(POINT_IN_TIME_NOT_SUPPORTED_WITH_EXPRESS_ONEZONE.to_string());
1582 }
1583 }
1584
1585 if Utc::now() < self.point_in_time.unwrap() {
1586 return Err(POINT_IN_TIME_VALUE_INVALID.to_string());
1587 }
1588
1589 Ok(())
1590 }
1591
1592 fn check_report_metadata_sync_status_conflict(&self) -> Result<(), String> {
1593 if !self.report_metadata_sync_status {
1594 return Ok(());
1595 }
1596
1597 let source = storage_path::parse_storage_path(&self.source);
1598 if matches!(source, StoragePath::Local(_)) {
1599 return Err(
1600 SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_REPORT_METADATA_SYNC_STATUS.to_string(),
1601 );
1602 }
1603
1604 let target = storage_path::parse_storage_path(&self.target);
1605 if matches!(target, StoragePath::Local(_)) {
1606 return Err(
1607 TARGET_LOCAL_STORAGE_SPECIFIED_WITH_REPORT_METADATA_SYNC_STATUS.to_string(),
1608 );
1609 }
1610
1611 Ok(())
1612 }
1613
1614 fn check_report_tagging_sync_status_conflict(&self) -> Result<(), String> {
1615 if !self.report_tagging_sync_status {
1616 return Ok(());
1617 }
1618
1619 let source = storage_path::parse_storage_path(&self.source);
1620 if matches!(source, StoragePath::Local(_)) {
1621 return Err(SOURCE_LOCAL_STORAGE_SPECIFIED_WITH_REPORT_TAGGING_SYNC_STATUS.to_string());
1622 }
1623
1624 let target = storage_path::parse_storage_path(&self.target);
1625 if matches!(target, StoragePath::Local(_)) {
1626 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_REPORT_TAGGING_SYNC_STATUS.to_string());
1627 }
1628
1629 Ok(())
1630 }
1631
1632 fn check_report_annotations_sync_status_conflict(&self) -> Result<(), String> {
1633 if !self.report_annotations_sync_status {
1634 return Ok(());
1635 }
1636
1637 let source = storage_path::parse_storage_path(&self.source);
1638 let target = storage_path::parse_storage_path(&self.target);
1639
1640 if !storage_path::is_both_storage_s3(&source, &target) {
1641 return Err(LOCAL_STORAGE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1642 }
1643
1644 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.source) {
1645 if is_express_onezone_storage(&bucket) {
1646 return Err(EXPRESS_ONEZONE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1647 }
1648 }
1649
1650 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.target) {
1651 if is_express_onezone_storage(&bucket) {
1652 return Err(EXPRESS_ONEZONE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1653 }
1654 }
1655
1656 Ok(())
1657 }
1658
1659 fn check_if_match_conflict(&self) -> Result<(), String> {
1660 if !self.if_match {
1661 return Ok(());
1662 }
1663
1664 let target = storage_path::parse_storage_path(&self.target);
1665 if matches!(target, StoragePath::Local(_)) {
1666 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_IF_MATCH.to_string());
1667 }
1668
1669 if self.remove_modified_filter && !self.head_each_target {
1670 return Err(IF_MATCH_CONFLICT.to_string());
1671 }
1672
1673 Ok(())
1674 }
1675
1676 fn check_if_none_match_conflict(&self) -> Result<(), String> {
1677 if !self.if_none_match {
1678 return Ok(());
1679 }
1680
1681 let target = storage_path::parse_storage_path(&self.target);
1682 if matches!(target, StoragePath::Local(_)) {
1683 return Err(TARGET_LOCAL_STORAGE_SPECIFIED_WITH_IF_NONE_MATCH.to_string());
1684 }
1685
1686 Ok(())
1687 }
1688
1689 fn check_copy_source_if_match_conflict(&self) -> Result<(), String> {
1690 if !self.copy_source_if_match {
1691 return Ok(());
1692 }
1693
1694 let source = storage_path::parse_storage_path(&self.source);
1695 let target = storage_path::parse_storage_path(&self.target);
1696
1697 if !storage_path::is_both_storage_s3(&source, &target) {
1698 return Err(LOCAL_STORAGE_SPECIFIED.to_string());
1699 }
1700
1701 Ok(())
1702 }
1703
1704 fn check_enable_sync_object_annotation_conflict(&self) -> Result<(), String> {
1705 if !self.enable_sync_object_annotations {
1706 return Ok(());
1707 }
1708
1709 let source = storage_path::parse_storage_path(&self.source);
1710 let target = storage_path::parse_storage_path(&self.target);
1711
1712 if !storage_path::is_both_storage_s3(&source, &target) {
1713 return Err(LOCAL_STORAGE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1714 }
1715
1716 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.source) {
1717 if is_express_onezone_storage(&bucket) {
1718 return Err(EXPRESS_ONEZONE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1719 }
1720 }
1721
1722 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.target) {
1723 if is_express_onezone_storage(&bucket) {
1724 return Err(EXPRESS_ONEZONE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1725 }
1726 }
1727
1728 Ok(())
1729 }
1730
1731 fn check_sync_latest_object_annotation_conflict(&self) -> Result<(), String> {
1732 if !self.sync_latest_object_annotations {
1733 return Ok(());
1734 }
1735
1736 let source = storage_path::parse_storage_path(&self.source);
1737 let target = storage_path::parse_storage_path(&self.target);
1738
1739 if !storage_path::is_both_storage_s3(&source, &target) {
1740 return Err(LOCAL_STORAGE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1741 }
1742
1743 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.source) {
1744 if is_express_onezone_storage(&bucket) {
1745 return Err(EXPRESS_ONEZONE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1746 }
1747 }
1748
1749 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&self.target) {
1750 if is_express_onezone_storage(&bucket) {
1751 return Err(EXPRESS_ONEZONE_SPECIFIED_FOR_OBJECT_ANNOTATION.to_string());
1752 }
1753 }
1754
1755 Ok(())
1756 }
1757
1758 fn build_client_configs(
1759 &self,
1760 request_checksum_calculation: RequestChecksumCalculation,
1761 ) -> (Option<ClientConfig>, Option<ClientConfig>) {
1762 let source_credential = if self.source_no_sign_request {
1763 Some(S3Credentials::NoSignRequest)
1764 } else if let Some(source_profile) = self.source_profile.clone() {
1765 Some(S3Credentials::Profile(source_profile))
1766 } else if self.source_access_key.is_some() {
1767 self.source_access_key
1768 .clone()
1769 .map(|access_key| S3Credentials::Credentials {
1770 access_keys: AccessKeys {
1771 access_key,
1772 secret_access_key: self
1773 .source_secret_access_key
1774 .as_ref()
1775 .unwrap()
1776 .to_string(),
1777 session_token: self.source_session_token.clone(),
1778 },
1779 })
1780 } else {
1781 Some(S3Credentials::FromEnvironment)
1782 };
1783
1784 let target_credential = if let Some(target_profile) = self.target_profile.clone() {
1785 Some(S3Credentials::Profile(target_profile))
1786 } else if self.target_access_key.is_some() {
1787 self.target_access_key
1788 .clone()
1789 .map(|access_key| S3Credentials::Credentials {
1790 access_keys: AccessKeys {
1791 access_key,
1792 secret_access_key: self
1793 .target_secret_access_key
1794 .as_ref()
1795 .unwrap()
1796 .to_string(),
1797 session_token: self.target_session_token.clone(),
1798 },
1799 })
1800 } else {
1801 Some(S3Credentials::FromEnvironment)
1802 };
1803
1804 let parallel_upload_semaphore =
1805 Arc::new(Semaphore::new(self.max_parallel_uploads as usize));
1806
1807 let source_request_payer = if self.source_request_payer {
1808 Some(RequestPayer::Requester)
1809 } else {
1810 None
1811 };
1812
1813 let source_client_config = source_credential.map(|source_credential| ClientConfig {
1814 client_config_location: ClientConfigLocation {
1815 aws_config_file: self.aws_config_file.clone(),
1816 aws_shared_credentials_file: self.aws_shared_credentials_file.clone(),
1817 },
1818 credential: source_credential,
1819 region: self.source_region.clone(),
1820 endpoint_url: self.source_endpoint_url.clone(),
1821 force_path_style: self.source_force_path_style,
1822 retry_config: RetryConfig {
1823 aws_max_attempts: self.aws_max_attempts,
1824 initial_backoff_milliseconds: self.initial_backoff_milliseconds,
1825 },
1826 cli_timeout_config: CLITimeoutConfig {
1827 operation_timeout_milliseconds: self.operation_timeout_milliseconds,
1828 operation_attempt_timeout_milliseconds: self.operation_attempt_timeout_milliseconds,
1829 connect_timeout_milliseconds: self.connect_timeout_milliseconds,
1830 read_timeout_milliseconds: self.read_timeout_milliseconds,
1831 },
1832 disable_stalled_stream_protection: self.disable_stalled_stream_protection,
1833 request_checksum_calculation: RequestChecksumCalculation::WhenRequired,
1834 parallel_upload_semaphore: parallel_upload_semaphore.clone(),
1835 accelerate: self.source_accelerate,
1836 request_payer: source_request_payer,
1837 });
1838
1839 let target_request_payer = if self.target_request_payer {
1840 Some(RequestPayer::Requester)
1841 } else {
1842 None
1843 };
1844
1845 let target_client_config = target_credential.map(|target_credential| ClientConfig {
1846 client_config_location: ClientConfigLocation {
1847 aws_config_file: self.aws_config_file.clone(),
1848 aws_shared_credentials_file: self.aws_shared_credentials_file.clone(),
1849 },
1850 credential: target_credential,
1851 region: self.target_region.clone(),
1852 endpoint_url: self.target_endpoint_url.clone(),
1853 force_path_style: self.target_force_path_style,
1854 retry_config: RetryConfig {
1855 aws_max_attempts: self.aws_max_attempts,
1856 initial_backoff_milliseconds: self.initial_backoff_milliseconds,
1857 },
1858 cli_timeout_config: CLITimeoutConfig {
1859 operation_timeout_milliseconds: self.operation_timeout_milliseconds,
1860 operation_attempt_timeout_milliseconds: self.operation_attempt_timeout_milliseconds,
1861 connect_timeout_milliseconds: self.connect_timeout_milliseconds,
1862 read_timeout_milliseconds: self.read_timeout_milliseconds,
1863 },
1864 disable_stalled_stream_protection: self.disable_stalled_stream_protection,
1865 request_checksum_calculation,
1866 parallel_upload_semaphore,
1867 accelerate: self.target_accelerate,
1868 request_payer: target_request_payer,
1869 });
1870
1871 (source_client_config, target_client_config)
1872 }
1873}
1874
1875impl TryFrom<CLIArgs> for Config {
1876 type Error = String;
1877
1878 fn try_from(value: CLIArgs) -> Result<Self, Self::Error> {
1879 value.validate_storage_config()?;
1880
1881 let original_cloned_value = value.clone();
1882
1883 let mut tracing_config = value.verbosity.log_level().map(|log_level| TracingConfig {
1884 tracing_level: log_level,
1885 json_tracing: value.json_tracing,
1886 aws_sdk_tracing: value.aws_sdk_tracing,
1887 span_events_tracing: value.span_events_tracing,
1888 disable_color_tracing: value.disable_color_tracing,
1889 stderr_tracing: value.tracing_stderr,
1890 });
1891
1892 let storage_class = value
1893 .storage_class
1894 .map(|storage_class| StorageClass::from_str(&storage_class).unwrap());
1895
1896 let sse = value
1897 .sse
1898 .map(|sse| ServerSideEncryption::from_str(&sse).unwrap());
1899
1900 let canned_acl = value
1901 .acl
1902 .map(|acl| ObjectCannedAcl::from_str(&acl).unwrap());
1903
1904 let include_regex = value
1905 .filter_include_regex
1906 .map(|regex| Regex::new(®ex).unwrap());
1907
1908 let exclude_regex = value
1909 .filter_exclude_regex
1910 .map(|regex| Regex::new(®ex).unwrap());
1911 let include_content_type_regex = value
1912 .filter_include_content_type_regex
1913 .map(|regex| Regex::new(®ex).unwrap());
1914 let exclude_content_type_regex = value
1915 .filter_exclude_content_type_regex
1916 .map(|regex| Regex::new(®ex).unwrap());
1917
1918 let include_metadata_regex = value
1919 .filter_include_metadata_regex
1920 .map(|regex| Regex::new(®ex).unwrap());
1921 let exclude_metadata_regex = value
1922 .filter_exclude_metadata_regex
1923 .map(|regex| Regex::new(®ex).unwrap());
1924 let include_tag_regex = value
1925 .filter_include_tag_regex
1926 .map(|regex| Regex::new(®ex).unwrap());
1927 let exclude_tag_regex = value
1928 .filter_exclude_tag_regex
1929 .map(|regex| Regex::new(®ex).unwrap());
1930
1931 let rate_limit_bandwidth = value
1932 .rate_limit_bandwidth
1933 .map(|bandwidth| human_bytes::parse_human_bandwidth(&bandwidth).unwrap());
1934
1935 let mut additional_checksum_algorithm = value
1936 .additional_checksum_algorithm
1937 .map(|algorithm| ChecksumAlgorithm::from(algorithm.as_str()));
1938
1939 let check_additional_checksum_algorithm = value
1940 .check_additional_checksum
1941 .map(|algorithm| ChecksumAlgorithm::from(algorithm.as_str()));
1942
1943 let check_mtime_and_additional_checksum = value
1944 .check_mtime_and_additional_checksum
1945 .map(|algorithm| ChecksumAlgorithm::from(algorithm.as_str()));
1946
1947 let mut checksum_mode = if value.enable_additional_checksum {
1948 Some(ChecksumMode::Enabled)
1949 } else {
1950 None
1951 };
1952
1953 let tagging = value
1954 .tagging
1955 .map(|tagging| tagging::parse_tagging(&tagging).unwrap());
1956 let filter_larger_size = value
1957 .filter_larger_size
1958 .map(|human_bytes| human_bytes::parse_human_bytes_without_limit(&human_bytes).unwrap());
1959 let filter_smaller_size = value
1960 .filter_smaller_size
1961 .map(|human_bytes| human_bytes::parse_human_bytes_without_limit(&human_bytes).unwrap());
1962
1963 let metadata = if value.metadata.is_some() {
1964 Some(metadata::parse_metadata(&value.metadata.unwrap())?)
1965 } else {
1966 None
1967 };
1968
1969 let mut full_object_checksum = if additional_checksum_algorithm
1970 .as_ref()
1971 .is_some_and(|algorithm| algorithm == &ChecksumAlgorithm::Crc64Nvme)
1972 {
1973 true
1974 } else {
1975 value.full_object_checksum
1976 };
1977
1978 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&value.source) {
1979 if is_express_onezone_storage(&bucket)
1980 && !value.disable_express_one_zone_additional_checksum
1981 {
1982 checksum_mode = Some(ChecksumMode::Enabled);
1983 }
1984 }
1985
1986 let mut request_checksum_calculation = RequestChecksumCalculation::WhenRequired;
1987 if let StoragePath::S3 { bucket, .. } = storage_path::parse_storage_path(&value.target) {
1988 if is_express_onezone_storage(&bucket)
1989 && additional_checksum_algorithm.is_none()
1990 && !value.disable_express_one_zone_additional_checksum
1991 {
1992 additional_checksum_algorithm = Some(ChecksumAlgorithm::Crc64Nvme);
1993 full_object_checksum = true;
1994 request_checksum_calculation = RequestChecksumCalculation::WhenSupported;
1995 } else if additional_checksum_algorithm.is_some() {
1996 request_checksum_calculation = RequestChecksumCalculation::WhenSupported;
1997 }
1998 }
1999
2000 let (source_client_config, target_client_config) =
2001 original_cloned_value.build_client_configs(request_checksum_calculation);
2002
2003 #[allow(unused_assignments)]
2004 #[allow(unused_mut)]
2005 let mut allow_e2e_test_dangerous_simulation = false;
2006
2007 #[allow(unused_assignments)]
2008 #[allow(unused_mut)]
2009 let mut cancellation_point = None;
2010 #[allow(unused_assignments)]
2011 #[allow(unused_mut)]
2012 let mut panic_simulation_point = None;
2013 #[allow(unused_assignments)]
2014 #[allow(unused_mut)]
2015 let mut error_simulation_point = None;
2016 #[cfg(e2e_test_dangerous_simulations)]
2017 {
2018 allow_e2e_test_dangerous_simulation = value.allow_e2e_test_dangerous_simulation;
2019 cancellation_point = value.cancellation_point;
2020 panic_simulation_point = value.panic_simulation_point;
2021 error_simulation_point = value.error_simulation_point;
2022 }
2023
2024 #[allow(unused_assignments)]
2025 #[allow(unused_mut)]
2026 let mut test_user_defined_callback = false;
2027 #[cfg(e2e_test_dangerous_simulations)]
2028 {
2029 test_user_defined_callback = value.test_user_defined_callback;
2030 }
2031
2032 let mut remove_modified_filter = if value.point_in_time.is_some() {
2035 true
2036 } else {
2037 value.remove_modified_filter
2038 };
2039
2040 let dry_run = if value.report_sync_status {
2042 true
2043 } else {
2044 value.dry_run
2045 };
2046
2047 let warn_as_error;
2050 let check_etag;
2051 let head_each_target;
2052 if value.report_sync_status {
2053 remove_modified_filter = true;
2054 warn_as_error = true;
2055 head_each_target = true;
2056 check_etag = check_additional_checksum_algorithm.is_none();
2057 } else {
2058 warn_as_error = value.warn_as_error;
2059 check_etag = value.check_etag;
2060 head_each_target = value.head_each_target;
2061 }
2062
2063 if dry_run {
2064 if tracing_config.is_none() {
2065 tracing_config = Some(TracingConfig {
2066 tracing_level: log::Level::Info,
2067 json_tracing: DEFAULT_JSON_TRACING,
2068 aws_sdk_tracing: DEFAULT_AWS_SDK_TRACING,
2069 span_events_tracing: DEFAULT_SPAN_EVENTS_TRACING,
2070 disable_color_tracing: DEFAULT_DISABLE_COLOR_TRACING,
2071 stderr_tracing: value.tracing_stderr,
2072 });
2073 } else if tracing_config.unwrap().tracing_level < log::Level::Info {
2074 tracing_config = Some(TracingConfig {
2075 tracing_level: log::Level::Info,
2076 json_tracing: tracing_config.unwrap().json_tracing,
2077 aws_sdk_tracing: tracing_config.unwrap().aws_sdk_tracing,
2078 span_events_tracing: tracing_config.unwrap().span_events_tracing,
2079 disable_color_tracing: tracing_config.unwrap().disable_color_tracing,
2080 stderr_tracing: tracing_config.unwrap().stderr_tracing,
2081 });
2082 }
2083 }
2084
2085 cfg_if! {
2086 if #[cfg(feature = "lua_support")] {
2087 let preprocess_callback_lua_script = value.preprocess_callback_lua_script.clone();
2088 let event_callback_lua_script = value.event_callback_lua_script.clone();
2089 let filter_callback_lua_script = value.filter_callback_lua_script.clone();
2090 let allow_lua_os_library = value.allow_lua_os_library;
2091 let allow_lua_unsafe_vm = value.allow_lua_unsafe_vm;
2092 let lua_vm_memory_limit = human_bytes::parse_human_bytes_without_low_limit(&value.lua_vm_memory_limit)? as usize;
2093 let lua_callback_timeout_milliseconds = value.lua_callback_timeout_milliseconds;
2094 } else {
2095 let preprocess_callback_lua_script = None;
2096 let event_callback_lua_script = None;
2097 let filter_callback_lua_script = None;
2098 let allow_lua_os_library = false;
2099 let allow_lua_unsafe_vm = false;
2100 let lua_vm_memory_limit = 0;
2101 let lua_callback_timeout_milliseconds: u64 = 0;
2102 }
2103 }
2104
2105 #[allow(unused_mut)]
2106 let mut preprocess_manager = PreprocessManager::new();
2107 cfg_if! {
2108 if #[cfg(feature = "lua_support")] {
2109 if let Some(preprocess_callback_lua_script) = preprocess_callback_lua_script.as_ref() {
2110 let mut lua_preprocess_callback = crate::callback::lua_preprocess_callback::LuaPreprocessCallback::new(
2111 lua_vm_memory_limit,
2112 allow_lua_os_library,
2113 allow_lua_unsafe_vm,
2114 lua_callback_timeout_milliseconds,
2115 );
2116 if let Err(e) =
2117 lua_preprocess_callback.load_and_compile(preprocess_callback_lua_script.as_str())
2118 {
2119 return Err(LUA_SCRIPT_LOAD_ERROR.to_string() + &e.to_string());
2120 }
2121 preprocess_manager.register_callback(lua_preprocess_callback);
2122 }
2123 }
2124 }
2125
2126 #[allow(unused_mut)]
2127 let mut event_manager = EventManager::new();
2128 cfg_if! {
2129 if #[cfg(feature = "lua_support")] {
2130 if let Some(event_callback_lua_script) = event_callback_lua_script.as_ref() {
2131 let mut lua_event_callback = crate::callback::lua_event_callback::LuaEventCallback::new(
2132 lua_vm_memory_limit,
2133 allow_lua_os_library,
2134 allow_lua_unsafe_vm,
2135 lua_callback_timeout_milliseconds,
2136 );
2137 if let Err(e) = lua_event_callback.load_and_compile(event_callback_lua_script.as_str())
2138 {
2139 return Err(LUA_SCRIPT_LOAD_ERROR.to_string() + &e.to_string());
2140 }
2141 event_manager.register_callback(EventType::ALL_EVENTS, lua_event_callback, dry_run);
2143 }
2144 }
2145 }
2146
2147 #[allow(unused_mut)]
2148 let mut filter_manager = FilterManager::new();
2149 cfg_if! {
2150 if #[cfg(feature = "lua_support")] {
2151 if let Some(filter_callback_lua_script) = filter_callback_lua_script.as_ref() {
2152 let mut lua_filter_callback = crate::callback::lua_filter_callback::LuaFilterCallback::new(
2153 lua_vm_memory_limit,
2154 allow_lua_os_library,
2155 allow_lua_unsafe_vm,
2156 lua_callback_timeout_milliseconds,
2157 );
2158 if let Err(e) =
2159 lua_filter_callback.load_and_compile(filter_callback_lua_script.as_str())
2160 {
2161 return Err(LUA_SCRIPT_LOAD_ERROR.to_string() + &e.to_string());
2162 }
2163 filter_manager.register_callback(lua_filter_callback);
2164 }
2165 }
2166 }
2167
2168 Ok(Config {
2169 source: storage_path::parse_storage_path(&value.source),
2170 target: storage_path::parse_storage_path(&value.target),
2171
2172 show_no_progress: value.show_no_progress,
2173
2174 source_client_config,
2175 target_client_config,
2176
2177 tracing_config,
2178
2179 force_retry_config: ForceRetryConfig {
2180 force_retry_count: value.force_retry_count,
2181 force_retry_interval_milliseconds: value.force_retry_interval_milliseconds,
2182 },
2183
2184 transfer_config: TransferConfig {
2185 multipart_threshold: human_bytes::parse_human_bytes(&value.multipart_threshold)?,
2186 multipart_chunksize: human_bytes::parse_human_bytes(&value.multipart_chunksize)?,
2187 auto_chunksize: value.auto_chunksize,
2188 },
2189
2190 worker_size: value.worker_size,
2191
2192 warn_as_error,
2193 follow_symlinks: !value.ignore_symlinks,
2194 head_each_target,
2195 sync_with_delete: value.delete,
2196 disable_tagging: value.disable_tagging,
2197 sync_latest_tagging: value.sync_latest_tagging,
2198 server_side_copy: value.server_side_copy,
2199 no_guess_mime_type: value.no_guess_mime_type,
2200 disable_multipart_verify: value.disable_multipart_verify,
2201 disable_etag_verify: value.disable_etag_verify,
2202 disable_additional_checksum_verify: value.disable_additional_checksum_verify,
2203 enable_versioning: value.enable_versioning,
2204 point_in_time: value.point_in_time,
2205 storage_class,
2206 sse,
2207 sse_kms_key_id: SseKmsKeyId {
2208 id: value.sse_kms_key_id,
2209 },
2210 source_sse_c: value.source_sse_c,
2211 source_sse_c_key: SseCustomerKey {
2212 key: value.source_sse_c_key,
2213 },
2214 source_sse_c_key_md5: value.source_sse_c_key_md5,
2215 target_sse_c: value.target_sse_c,
2216 target_sse_c_key: SseCustomerKey {
2217 key: value.target_sse_c_key,
2218 },
2219 target_sse_c_key_md5: value.target_sse_c_key_md5,
2220 canned_acl,
2221 additional_checksum_algorithm,
2222 additional_checksum_mode: checksum_mode,
2223 dry_run,
2224 rate_limit_objects: value.rate_limit_objects,
2225 rate_limit_bandwidth,
2226 max_parallel_listings: value.max_parallel_listings,
2227 object_listing_queue_size: value.object_listing_queue_size,
2228 max_parallel_listing_max_depth: value.max_parallel_listing_max_depth,
2229 allow_parallel_listings_in_express_one_zone: value
2230 .allow_parallel_listings_in_express_one_zone,
2231 cache_control: value.cache_control,
2232 content_disposition: value.content_disposition,
2233 content_encoding: value.content_encoding,
2234 content_language: value.content_language,
2235 content_type: value.content_type,
2236 expires: value.expires,
2237 metadata,
2238 website_redirect: value.website_redirect,
2239 no_sync_system_metadata: value.no_sync_system_metadata,
2240 no_sync_user_defined_metadata: value.no_sync_user_defined_metadata,
2241 tagging,
2242 filter_config: FilterConfig {
2243 before_time: value.filter_mtime_before,
2244 after_time: value.filter_mtime_after,
2245 remove_modified_filter,
2246 check_size: value.check_size,
2247 check_etag,
2248 check_mtime_and_etag: value.check_mtime_and_etag,
2249 check_checksum_algorithm: check_additional_checksum_algorithm,
2250 check_mtime_and_additional_checksum,
2251 include_regex,
2252 exclude_regex,
2253 include_content_type_regex,
2254 exclude_content_type_regex,
2255 include_metadata_regex,
2256 exclude_metadata_regex,
2257 include_tag_regex,
2258 exclude_tag_regex,
2259 larger_size: filter_larger_size,
2260 smaller_size: filter_smaller_size,
2261 filter_manager,
2262 },
2263 max_keys: value.max_keys,
2264 put_last_modified_metadata: value.put_last_modified_metadata,
2265 auto_complete_shell: value.auto_complete_shell,
2266 disable_payload_signing: value.disable_payload_signing,
2267 disable_content_md5_header: value.disable_content_md5_header,
2268 delete_excluded: value.delete_excluded,
2269 full_object_checksum,
2270 allow_e2e_test_dangerous_simulation,
2271 test_user_defined_callback,
2272 cancellation_point,
2273 panic_simulation_point,
2274 error_simulation_point,
2275 source_accelerate: value.source_accelerate,
2276 target_accelerate: value.target_accelerate,
2277 source_request_payer: value.source_request_payer,
2278 target_request_payer: value.target_request_payer,
2279 report_sync_status: value.report_sync_status,
2280 report_metadata_sync_status: value.report_metadata_sync_status,
2281 report_tagging_sync_status: value.report_tagging_sync_status,
2282 report_annotations_sync_status: value.report_annotations_sync_status,
2283 event_manager,
2284 preprocess_manager,
2285 preprocess_callback_lua_script,
2286 event_callback_lua_script,
2287 filter_callback_lua_script,
2288 allow_lua_os_library,
2289 allow_lua_unsafe_vm,
2290 lua_vm_memory_limit,
2291 lua_callback_timeout_milliseconds,
2292 if_match: value.if_match,
2293 if_none_match: value.if_none_match,
2294 copy_source_if_match: value.copy_source_if_match,
2295 max_delete: value.max_delete,
2296 ignore_glacier_warnings: value.ignore_glacier_warnings,
2297 enable_sync_object_annotations: value.enable_sync_object_annotations,
2298 sync_latest_object_annotations: value.sync_latest_object_annotations,
2299 disable_check_annotation_etag: value.disable_check_annotation_etag,
2300 })
2301 }
2302}
2303
2304fn is_express_onezone_storage(bucket: &str) -> bool {
2305 bucket.ends_with(EXPRESS_ONEZONE_STORAGE_SUFFIX)
2306}