1use std::{collections::BTreeMap, fmt};
9
10use serde::{Serialize, Serializer};
11use serde_json::{Map, Value};
12
13use crate::file_extension::normalize_file_extension;
14
15pub type ExtraOptions = BTreeMap<String, Value>;
17
18#[derive(Clone, Debug, PartialEq, Eq, Serialize, serde::Deserialize)]
24#[serde(untagged)]
25#[non_exhaustive]
26pub enum Input {
27 Task(String),
28 Tasks(Vec<String>),
29}
30
31impl From<String> for Input {
32 fn from(value: String) -> Self {
33 Self::Task(value)
34 }
35}
36
37impl From<&str> for Input {
38 fn from(value: &str) -> Self {
39 Self::Task(value.to_string())
40 }
41}
42
43impl From<Vec<String>> for Input {
44 fn from(value: Vec<String>) -> Self {
45 Self::Tasks(value)
46 }
47}
48
49impl From<Vec<&str>> for Input {
50 fn from(value: Vec<&str>) -> Self {
51 Self::Tasks(value.into_iter().map(str::to_string).collect())
52 }
53}
54
55#[derive(Clone)]
75pub struct TaskRequest {
76 operation: String,
77 payload: Map<String, Value>,
78}
79
80mod sealed {
81 pub trait Sealed {}
82}
83
84pub trait TaskPayload: sealed::Sealed + Serialize {
89 const OPERATION: &'static str;
91}
92
93impl TaskRequest {
94 pub fn import_url(url: impl Into<String>) -> Self {
96 ImportUrlTask::new(url).into()
97 }
98
99 pub fn import_upload() -> Self {
101 ImportUploadTask::default().into()
102 }
103
104 pub fn import_base64(file: impl Into<String>, filename: impl Into<String>) -> Self {
106 Base64ImportTask::new(file, filename).into()
107 }
108
109 pub fn import_raw(file: impl Into<String>, filename: impl Into<String>) -> Self {
111 RawImportTask::new(file, filename).into()
112 }
113
114 pub fn import_s3(
116 bucket: impl Into<String>,
117 region: impl Into<String>,
118 access_key_id: impl Into<String>,
119 secret_access_key: impl Into<String>,
120 ) -> Self {
121 S3ImportTask::new(bucket, region, access_key_id, secret_access_key).into()
122 }
123
124 pub fn import_azure_blob(
126 storage_account: impl Into<String>,
127 container: impl Into<String>,
128 ) -> Self {
129 AzureBlobImportTask::new(storage_account, container).into()
130 }
131
132 pub fn import_google_cloud_storage(
134 project_id: impl Into<String>,
135 bucket: impl Into<String>,
136 client_email: impl Into<String>,
137 private_key: impl Into<String>,
138 ) -> Self {
139 GoogleCloudStorageImportTask::new(project_id, bucket, client_email, private_key).into()
140 }
141
142 pub fn import_openstack(
144 auth_url: impl Into<String>,
145 username: impl Into<String>,
146 password: impl Into<String>,
147 region: impl Into<String>,
148 container: impl Into<String>,
149 ) -> Self {
150 OpenStackImportTask::new(auth_url, username, password, region, container).into()
151 }
152
153 pub fn import_sftp(host: impl Into<String>, username: impl Into<String>) -> Self {
155 SftpImportTask::new(host, username).into()
156 }
157
158 pub fn convert(input: impl Into<Input>, output_format: impl Into<String>) -> Self {
160 ConvertTask::new(input, output_format).into()
161 }
162
163 pub fn optimize(input: impl Into<Input>) -> Self {
165 OptimizeTask::new(input).into()
166 }
167
168 pub fn watermark(task: WatermarkTask) -> Self {
170 task.into()
171 }
172
173 pub fn capture_website(url: impl Into<String>, output_format: impl Into<String>) -> Self {
175 CaptureWebsiteTask::new(url, output_format).into()
176 }
177
178 pub fn thumbnail(input: impl Into<Input>, output_format: impl Into<String>) -> Self {
180 ThumbnailTask::new(input, output_format).into()
181 }
182
183 pub fn metadata(input: impl Into<Input>) -> Self {
185 MetadataTask::new(input).into()
186 }
187
188 pub fn metadata_write(input: impl Into<Input>) -> Self {
190 MetadataWriteTask::new(input).into()
191 }
192
193 pub fn merge(input: impl Into<Input>, output_format: impl Into<String>) -> Self {
195 MergeTask::new(input, output_format).into()
196 }
197
198 pub fn archive(input: impl Into<Input>, output_format: impl Into<String>) -> Self {
200 ArchiveTask::new(input, output_format).into()
201 }
202
203 pub fn command(
205 input: impl Into<Input>,
206 engine: impl Into<String>,
207 command: impl Into<String>,
208 arguments: impl Into<String>,
209 ) -> Self {
210 CommandTask::new(input, engine, command, arguments).into()
211 }
212
213 pub fn pdf_a(input: impl Into<Input>) -> Self {
215 PdfATask::new(input).into()
216 }
217
218 pub fn pdf_x(input: impl Into<Input>) -> Self {
220 PdfXTask::new(input).into()
221 }
222
223 pub fn pdf_ocr(input: impl Into<Input>) -> Self {
225 PdfOcrTask::new(input).into()
226 }
227
228 pub fn pdf_encrypt(input: impl Into<Input>) -> Self {
230 PdfEncryptTask::new(input).into()
231 }
232
233 pub fn pdf_decrypt(input: impl Into<Input>) -> Self {
235 PdfDecryptTask::new(input).into()
236 }
237
238 pub fn pdf_split_pages(input: impl Into<Input>) -> Self {
240 PdfSplitPagesTask::new(input).into()
241 }
242
243 pub fn pdf_extract_pages(input: impl Into<Input>) -> Self {
245 PdfExtractPagesTask::new(input).into()
246 }
247
248 pub fn pdf_rotate_pages(input: impl Into<Input>) -> Self {
250 PdfRotatePagesTask::new(input).into()
251 }
252
253 pub fn export_url(input: impl Into<Input>) -> Self {
255 ExportUrlTask::new(input).into()
256 }
257
258 pub fn export_s3(
260 input: impl Into<Input>,
261 bucket: impl Into<String>,
262 region: impl Into<String>,
263 access_key_id: impl Into<String>,
264 secret_access_key: impl Into<String>,
265 ) -> Self {
266 S3ExportTask::new(input, bucket, region, access_key_id, secret_access_key).into()
267 }
268
269 pub fn export_azure_blob(
271 input: impl Into<Input>,
272 storage_account: impl Into<String>,
273 container: impl Into<String>,
274 ) -> Self {
275 AzureBlobExportTask::new(input, storage_account, container).into()
276 }
277
278 pub fn export_google_cloud_storage(
280 input: impl Into<Input>,
281 project_id: impl Into<String>,
282 bucket: impl Into<String>,
283 client_email: impl Into<String>,
284 private_key: impl Into<String>,
285 ) -> Self {
286 GoogleCloudStorageExportTask::new(input, project_id, bucket, client_email, private_key)
287 .into()
288 }
289
290 pub fn export_openstack(
292 input: impl Into<Input>,
293 auth_url: impl Into<String>,
294 username: impl Into<String>,
295 password: impl Into<String>,
296 region: impl Into<String>,
297 container: impl Into<String>,
298 ) -> Self {
299 OpenStackExportTask::new(input, auth_url, username, password, region, container).into()
300 }
301
302 pub fn export_sftp(
304 input: impl Into<Input>,
305 host: impl Into<String>,
306 username: impl Into<String>,
307 ) -> Self {
308 SftpExportTask::new(input, host, username).into()
309 }
310
311 pub fn export_upload(input: impl Into<Input>, url: impl Into<String>) -> Self {
313 ExportUploadTask::new(input, url).into()
314 }
315
316 pub fn custom(operation: impl Into<String>) -> GenericTask {
329 GenericTask::new(operation)
330 }
331
332 pub fn try_from_payload<T>(payload: T) -> crate::Result<Self>
334 where
335 T: TaskPayload,
336 {
337 Ok(Self {
338 operation: T::OPERATION.to_string(),
339 payload: serialize_payload(payload)?,
340 })
341 }
342
343 pub fn from_payload<T>(payload: T) -> Self
348 where
349 T: TaskPayload,
350 {
351 Self::try_from_payload(payload)
352 .expect("task payload serialization should produce a JSON object")
353 }
354
355 pub fn operation(&self) -> &str {
357 self.operation.as_str()
358 }
359
360 pub fn payload(&self) -> &Map<String, Value> {
362 &self.payload
363 }
364}
365
366impl Serialize for TaskRequest {
367 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
368 where
369 S: Serializer,
370 {
371 let mut object = self.payload.clone();
372 object.insert(
373 "operation".to_string(),
374 Value::String(self.operation.clone()),
375 );
376
377 object.serialize(serializer)
378 }
379}
380
381impl fmt::Debug for TaskRequest {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 f.debug_struct("TaskRequest")
384 .field("operation", &self.operation)
385 .field("payload", &"REDACTED")
386 .finish()
387 }
388}
389
390macro_rules! task_payload {
391 ($type:ty, $operation:literal) => {
392 impl sealed::Sealed for $type {}
393
394 impl TaskPayload for $type {
395 const OPERATION: &'static str = $operation;
396 }
397
398 impl From<$type> for TaskRequest {
399 fn from(value: $type) -> Self {
400 Self::from_payload(value)
401 }
402 }
403 };
404}
405
406fn serialize_payload<T>(payload: T) -> crate::Result<Map<String, Value>>
407where
408 T: Serialize,
409{
410 match serde_json::to_value(payload)? {
411 Value::Object(object) => Ok(object),
412 _ => Err(<serde_json::Error as serde::ser::Error>::custom(
413 "task payload serialization must produce a JSON object",
414 )
415 .into()),
416 }
417}
418
419fn insert_extra_object_field(
420 extra: &mut ExtraOptions,
421 object_key: &str,
422 field_key: impl Into<String>,
423 value: impl Into<Value>,
424) {
425 match extra
426 .entry(object_key.to_string())
427 .or_insert_with(|| Value::Object(Map::new()))
428 {
429 Value::Object(object) => {
430 object.insert(field_key.into(), value.into());
431 }
432 existing => {
433 let mut object = Map::new();
434 object.insert(field_key.into(), value.into());
435 *existing = Value::Object(object);
436 }
437 }
438}
439
440fn redacted_option<T>(value: &Option<T>) -> Option<&'static str> {
441 value.as_ref().map(|_| "REDACTED")
442}
443
444struct RedactedStringMap<'a>(&'a BTreeMap<String, String>);
445
446impl fmt::Debug for RedactedStringMap<'_> {
447 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448 let mut debug = f.debug_map();
449 for key in self.0.keys() {
450 debug.entry(key, &"REDACTED");
451 }
452 debug.finish()
453 }
454}
455
456macro_rules! debug_struct_redacted {
457 (
458 $name:ident {
459 fields: [$($field:ident),* $(,)?],
460 redacted: [$($redacted:ident),* $(,)?],
461 redacted_options: [$($redacted_option:ident),* $(,)?],
462 redacted_maps: [$($redacted_map:ident),* $(,)?]
463 }
464 ) => {
465 impl fmt::Debug for $name {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 let mut debug = f.debug_struct(stringify!($name));
468 $(debug.field(stringify!($field), &self.$field);)*
469 $(debug.field(stringify!($redacted), &"REDACTED");)*
470 $(debug.field(stringify!($redacted_option), &redacted_option(&self.$redacted_option));)*
471 $(debug.field(stringify!($redacted_map), &RedactedStringMap(&self.$redacted_map));)*
472 debug.finish()
473 }
474 }
475 };
476}
477
478#[derive(Clone, Serialize)]
479pub struct ImportUrlTask {
480 url: String,
481 #[serde(skip_serializing_if = "Option::is_none")]
482 filename: Option<String>,
483 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
484 headers: BTreeMap<String, String>,
485}
486
487impl ImportUrlTask {
488 pub fn new(url: impl Into<String>) -> Self {
489 Self {
490 url: url.into(),
491 filename: None,
492 headers: BTreeMap::new(),
493 }
494 }
495
496 pub fn filename(mut self, filename: impl Into<String>) -> Self {
497 self.filename = Some(filename.into());
498 self
499 }
500
501 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
502 self.headers.insert(key.into(), value.into());
503 self
504 }
505}
506
507task_payload!(ImportUrlTask, "import/url");
508
509#[derive(Clone, Debug, Default, Serialize)]
510pub struct ImportUploadTask {
511 #[serde(skip_serializing_if = "Option::is_none")]
512 redirect: Option<String>,
513}
514
515impl ImportUploadTask {
516 pub fn redirect(mut self, redirect: impl Into<String>) -> Self {
517 self.redirect = Some(redirect.into());
518 self
519 }
520}
521
522task_payload!(ImportUploadTask, "import/upload");
523
524#[derive(Clone, Debug, Serialize)]
525pub struct Base64ImportTask {
526 file: String,
527 filename: String,
528}
529
530impl Base64ImportTask {
531 pub fn new(file: impl Into<String>, filename: impl Into<String>) -> Self {
532 Self {
533 file: file.into(),
534 filename: filename.into(),
535 }
536 }
537}
538
539task_payload!(Base64ImportTask, "import/base64");
540
541#[derive(Clone, Debug, Serialize)]
542pub struct RawImportTask {
543 file: String,
544 filename: String,
545}
546
547impl RawImportTask {
548 pub fn new(file: impl Into<String>, filename: impl Into<String>) -> Self {
549 Self {
550 file: file.into(),
551 filename: filename.into(),
552 }
553 }
554}
555
556task_payload!(RawImportTask, "import/raw");
557
558#[derive(Clone, Serialize)]
559pub struct S3ImportTask {
560 bucket: String,
561 region: String,
562 #[serde(skip_serializing_if = "Option::is_none")]
563 endpoint: Option<String>,
564 #[serde(skip_serializing_if = "Option::is_none")]
565 key: Option<String>,
566 #[serde(skip_serializing_if = "Option::is_none")]
567 key_prefix: Option<String>,
568 access_key_id: String,
569 secret_access_key: String,
570 #[serde(skip_serializing_if = "Option::is_none")]
571 session_token: Option<String>,
572 #[serde(skip_serializing_if = "Option::is_none")]
573 filename: Option<String>,
574}
575
576impl S3ImportTask {
577 pub fn new(
578 bucket: impl Into<String>,
579 region: impl Into<String>,
580 access_key_id: impl Into<String>,
581 secret_access_key: impl Into<String>,
582 ) -> Self {
583 Self {
584 bucket: bucket.into(),
585 region: region.into(),
586 endpoint: None,
587 key: None,
588 key_prefix: None,
589 access_key_id: access_key_id.into(),
590 secret_access_key: secret_access_key.into(),
591 session_token: None,
592 filename: None,
593 }
594 }
595
596 pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
597 self.endpoint = Some(endpoint.into());
598 self
599 }
600
601 pub fn key(mut self, key: impl Into<String>) -> Self {
602 self.key = Some(key.into());
603 self
604 }
605
606 pub fn key_prefix(mut self, key_prefix: impl Into<String>) -> Self {
607 self.key_prefix = Some(key_prefix.into());
608 self
609 }
610
611 pub fn session_token(mut self, session_token: impl Into<String>) -> Self {
612 self.session_token = Some(session_token.into());
613 self
614 }
615
616 pub fn filename(mut self, filename: impl Into<String>) -> Self {
617 self.filename = Some(filename.into());
618 self
619 }
620}
621
622task_payload!(S3ImportTask, "import/s3");
623
624#[derive(Clone, Serialize)]
625pub struct AzureBlobImportTask {
626 storage_account: String,
627 #[serde(skip_serializing_if = "Option::is_none")]
628 storage_access_key: Option<String>,
629 #[serde(skip_serializing_if = "Option::is_none")]
630 sas_token: Option<String>,
631 container: String,
632 #[serde(skip_serializing_if = "Option::is_none")]
633 blob: Option<String>,
634 #[serde(skip_serializing_if = "Option::is_none")]
635 blob_prefix: Option<String>,
636 #[serde(skip_serializing_if = "Option::is_none")]
637 filename: Option<String>,
638}
639
640impl AzureBlobImportTask {
641 pub fn new(storage_account: impl Into<String>, container: impl Into<String>) -> Self {
642 Self {
643 storage_account: storage_account.into(),
644 storage_access_key: None,
645 sas_token: None,
646 container: container.into(),
647 blob: None,
648 blob_prefix: None,
649 filename: None,
650 }
651 }
652
653 pub fn storage_access_key(mut self, storage_access_key: impl Into<String>) -> Self {
654 self.storage_access_key = Some(storage_access_key.into());
655 self
656 }
657
658 pub fn sas_token(mut self, sas_token: impl Into<String>) -> Self {
659 self.sas_token = Some(sas_token.into());
660 self
661 }
662
663 pub fn blob(mut self, blob: impl Into<String>) -> Self {
664 self.blob = Some(blob.into());
665 self
666 }
667
668 pub fn blob_prefix(mut self, blob_prefix: impl Into<String>) -> Self {
669 self.blob_prefix = Some(blob_prefix.into());
670 self
671 }
672
673 pub fn filename(mut self, filename: impl Into<String>) -> Self {
674 self.filename = Some(filename.into());
675 self
676 }
677}
678
679task_payload!(AzureBlobImportTask, "import/azure/blob");
680
681#[derive(Clone, Serialize)]
682pub struct GoogleCloudStorageImportTask {
683 project_id: String,
684 bucket: String,
685 client_email: String,
686 private_key: String,
687 #[serde(skip_serializing_if = "Option::is_none")]
688 file: Option<String>,
689 #[serde(skip_serializing_if = "Option::is_none")]
690 file_prefix: Option<String>,
691 #[serde(skip_serializing_if = "Option::is_none")]
692 filename: Option<String>,
693}
694
695impl GoogleCloudStorageImportTask {
696 pub fn new(
697 project_id: impl Into<String>,
698 bucket: impl Into<String>,
699 client_email: impl Into<String>,
700 private_key: impl Into<String>,
701 ) -> Self {
702 Self {
703 project_id: project_id.into(),
704 bucket: bucket.into(),
705 client_email: client_email.into(),
706 private_key: private_key.into(),
707 file: None,
708 file_prefix: None,
709 filename: None,
710 }
711 }
712
713 pub fn file(mut self, file: impl Into<String>) -> Self {
714 self.file = Some(file.into());
715 self
716 }
717
718 pub fn file_prefix(mut self, file_prefix: impl Into<String>) -> Self {
719 self.file_prefix = Some(file_prefix.into());
720 self
721 }
722
723 pub fn filename(mut self, filename: impl Into<String>) -> Self {
724 self.filename = Some(filename.into());
725 self
726 }
727}
728
729task_payload!(GoogleCloudStorageImportTask, "import/google-cloud-storage");
730
731#[derive(Clone, Serialize)]
732pub struct OpenStackImportTask {
733 auth_url: String,
734 username: String,
735 password: String,
736 region: String,
737 container: String,
738 #[serde(skip_serializing_if = "Option::is_none")]
739 file: Option<String>,
740 #[serde(skip_serializing_if = "Option::is_none")]
741 file_prefix: Option<String>,
742 #[serde(skip_serializing_if = "Option::is_none")]
743 filename: Option<String>,
744}
745
746impl OpenStackImportTask {
747 pub fn new(
748 auth_url: impl Into<String>,
749 username: impl Into<String>,
750 password: impl Into<String>,
751 region: impl Into<String>,
752 container: impl Into<String>,
753 ) -> Self {
754 Self {
755 auth_url: auth_url.into(),
756 username: username.into(),
757 password: password.into(),
758 region: region.into(),
759 container: container.into(),
760 file: None,
761 file_prefix: None,
762 filename: None,
763 }
764 }
765
766 pub fn file(mut self, file: impl Into<String>) -> Self {
767 self.file = Some(file.into());
768 self
769 }
770
771 pub fn file_prefix(mut self, file_prefix: impl Into<String>) -> Self {
772 self.file_prefix = Some(file_prefix.into());
773 self
774 }
775
776 pub fn filename(mut self, filename: impl Into<String>) -> Self {
777 self.filename = Some(filename.into());
778 self
779 }
780}
781
782task_payload!(OpenStackImportTask, "import/openstack");
783
784#[derive(Clone, Serialize)]
785pub struct SftpImportTask {
786 host: String,
787 #[serde(skip_serializing_if = "Option::is_none")]
788 port: Option<u16>,
789 username: String,
790 #[serde(skip_serializing_if = "Option::is_none")]
791 password: Option<String>,
792 #[serde(skip_serializing_if = "Option::is_none")]
793 private_key: Option<String>,
794 #[serde(skip_serializing_if = "Option::is_none")]
795 file: Option<String>,
796 #[serde(skip_serializing_if = "Option::is_none")]
797 path: Option<String>,
798 #[serde(skip_serializing_if = "Option::is_none")]
799 filename: Option<String>,
800}
801
802impl SftpImportTask {
803 pub fn new(host: impl Into<String>, username: impl Into<String>) -> Self {
804 Self {
805 host: host.into(),
806 port: None,
807 username: username.into(),
808 password: None,
809 private_key: None,
810 file: None,
811 path: None,
812 filename: None,
813 }
814 }
815
816 pub fn port(mut self, port: u16) -> Self {
817 self.port = Some(port);
818 self
819 }
820
821 pub fn password(mut self, password: impl Into<String>) -> Self {
822 self.password = Some(password.into());
823 self
824 }
825
826 pub fn private_key(mut self, private_key: impl Into<String>) -> Self {
827 self.private_key = Some(private_key.into());
828 self
829 }
830
831 pub fn file(mut self, file: impl Into<String>) -> Self {
832 self.file = Some(file.into());
833 self
834 }
835
836 pub fn path(mut self, path: impl Into<String>) -> Self {
837 self.path = Some(path.into());
838 self
839 }
840
841 pub fn filename(mut self, filename: impl Into<String>) -> Self {
842 self.filename = Some(filename.into());
843 self
844 }
845}
846
847task_payload!(SftpImportTask, "import/sftp");
848
849#[derive(Clone, Debug, Serialize)]
850pub struct ConvertTask {
851 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
852 extra: ExtraOptions,
853 input: Input,
854 #[serde(skip_serializing_if = "Option::is_none")]
855 input_format: Option<String>,
856 output_format: String,
857 #[serde(skip_serializing_if = "Option::is_none")]
858 engine: Option<String>,
859 #[serde(skip_serializing_if = "Option::is_none")]
860 engine_version: Option<String>,
861 #[serde(skip_serializing_if = "Option::is_none")]
862 filename: Option<String>,
863 #[serde(skip_serializing_if = "Option::is_none")]
864 timeout: Option<u64>,
865}
866
867impl ConvertTask {
868 pub fn new(input: impl Into<Input>, output_format: impl Into<String>) -> Self {
869 Self {
870 input: input.into(),
871 input_format: None,
872 output_format: normalize_file_extension(output_format),
873 engine: None,
874 engine_version: None,
875 filename: None,
876 timeout: None,
877 extra: BTreeMap::new(),
878 }
879 }
880
881 pub fn input_format(mut self, input_format: impl Into<String>) -> Self {
882 self.input_format = Some(normalize_file_extension(input_format));
883 self
884 }
885
886 pub fn engine(mut self, engine: impl Into<String>) -> Self {
887 self.engine = Some(engine.into());
888 self
889 }
890
891 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
892 self.engine_version = Some(engine_version.into());
893 self
894 }
895
896 pub fn filename(mut self, filename: impl Into<String>) -> Self {
897 self.filename = Some(filename.into());
898 self
899 }
900
901 pub fn timeout(mut self, timeout: u64) -> Self {
902 self.timeout = Some(timeout);
903 self
904 }
905
906 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
907 self.extra.insert(key.into(), value.into());
908 self
909 }
910}
911
912task_payload!(ConvertTask, "convert");
913
914#[derive(Clone, Debug, Serialize)]
915pub struct OptimizeTask {
916 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
917 extra: ExtraOptions,
918 input: Input,
919 #[serde(skip_serializing_if = "Option::is_none")]
920 input_format: Option<String>,
921 #[serde(skip_serializing_if = "Option::is_none")]
922 quality: Option<u8>,
923 #[serde(skip_serializing_if = "Option::is_none")]
924 profile: Option<String>,
925 #[serde(skip_serializing_if = "Option::is_none")]
926 engine: Option<String>,
927 #[serde(skip_serializing_if = "Option::is_none")]
928 engine_version: Option<String>,
929 #[serde(skip_serializing_if = "Option::is_none")]
930 filename: Option<String>,
931 #[serde(skip_serializing_if = "Option::is_none")]
932 timeout: Option<u64>,
933}
934
935impl OptimizeTask {
936 pub fn new(input: impl Into<Input>) -> Self {
937 Self {
938 input: input.into(),
939 input_format: None,
940 quality: None,
941 profile: None,
942 engine: None,
943 engine_version: None,
944 filename: None,
945 timeout: None,
946 extra: BTreeMap::new(),
947 }
948 }
949
950 pub fn input_format(mut self, input_format: impl Into<String>) -> Self {
951 self.input_format = Some(normalize_file_extension(input_format));
952 self
953 }
954
955 pub fn quality(mut self, quality: u8) -> Self {
956 self.quality = Some(quality);
957 self
958 }
959
960 pub fn profile(mut self, profile: impl Into<String>) -> Self {
961 self.profile = Some(profile.into());
962 self
963 }
964
965 pub fn engine(mut self, engine: impl Into<String>) -> Self {
966 self.engine = Some(engine.into());
967 self
968 }
969
970 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
971 self.engine_version = Some(engine_version.into());
972 self
973 }
974
975 pub fn filename(mut self, filename: impl Into<String>) -> Self {
976 self.filename = Some(filename.into());
977 self
978 }
979
980 pub fn timeout(mut self, timeout: u64) -> Self {
981 self.timeout = Some(timeout);
982 self
983 }
984
985 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
986 self.extra.insert(key.into(), value.into());
987 self
988 }
989}
990
991task_payload!(OptimizeTask, "optimize");
992
993#[derive(Clone, Debug, Serialize)]
994#[serde(rename_all = "lowercase")]
995#[non_exhaustive]
996pub enum Layer {
997 Above,
998 Below,
999}
1000
1001#[derive(Clone, Debug, Serialize)]
1002#[serde(rename_all = "lowercase")]
1003#[non_exhaustive]
1004pub enum PositionVertical {
1005 Top,
1006 Center,
1007 Bottom,
1008}
1009
1010#[derive(Clone, Debug, Serialize)]
1011#[serde(rename_all = "lowercase")]
1012#[non_exhaustive]
1013pub enum PositionHorizontal {
1014 Left,
1015 Center,
1016 Right,
1017}
1018
1019#[derive(Clone, Debug, Serialize)]
1020#[serde(rename_all = "lowercase")]
1021#[non_exhaustive]
1022pub enum FontAlign {
1023 Left,
1024 Center,
1025 Right,
1026}
1027
1028#[derive(Clone, Debug, Serialize)]
1029pub struct WatermarkTask {
1030 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1031 extra: ExtraOptions,
1032 input: Input,
1033 #[serde(skip_serializing_if = "Option::is_none")]
1034 input_format: Option<String>,
1035 #[serde(skip_serializing_if = "Option::is_none")]
1036 pages: Option<String>,
1037 #[serde(skip_serializing_if = "Option::is_none")]
1038 layer: Option<Layer>,
1039 #[serde(skip_serializing_if = "Option::is_none")]
1040 text: Option<String>,
1041 #[serde(skip_serializing_if = "Option::is_none")]
1042 font_size: Option<u32>,
1043 #[serde(skip_serializing_if = "Option::is_none")]
1044 font_width_percent: Option<u8>,
1045 #[serde(skip_serializing_if = "Option::is_none")]
1046 font_color: Option<String>,
1047 #[serde(skip_serializing_if = "Option::is_none")]
1048 font_name: Option<String>,
1049 #[serde(skip_serializing_if = "Option::is_none")]
1050 font_align: Option<FontAlign>,
1051 #[serde(skip_serializing_if = "Option::is_none")]
1052 image: Option<String>,
1053 #[serde(skip_serializing_if = "Option::is_none")]
1054 image_width: Option<u32>,
1055 #[serde(skip_serializing_if = "Option::is_none")]
1056 image_height: Option<u32>,
1057 #[serde(skip_serializing_if = "Option::is_none")]
1058 image_width_percent: Option<u8>,
1059 #[serde(skip_serializing_if = "Option::is_none")]
1060 position_vertical: Option<PositionVertical>,
1061 #[serde(skip_serializing_if = "Option::is_none")]
1062 position_horizontal: Option<PositionHorizontal>,
1063 #[serde(skip_serializing_if = "Option::is_none")]
1064 margin_vertical: Option<u32>,
1065 #[serde(skip_serializing_if = "Option::is_none")]
1066 margin_horizontal: Option<u32>,
1067 #[serde(skip_serializing_if = "Option::is_none")]
1068 opacity: Option<u8>,
1069 #[serde(skip_serializing_if = "Option::is_none")]
1070 rotation: Option<i16>,
1071 #[serde(skip_serializing_if = "Option::is_none")]
1072 filename: Option<String>,
1073 #[serde(skip_serializing_if = "Option::is_none")]
1074 engine: Option<String>,
1075 #[serde(skip_serializing_if = "Option::is_none")]
1076 engine_version: Option<String>,
1077 #[serde(skip_serializing_if = "Option::is_none")]
1078 timeout: Option<u64>,
1079}
1080
1081impl WatermarkTask {
1082 pub fn text(input: impl Into<Input>, text: impl Into<String>) -> Self {
1083 Self::new(input).text_content(text)
1084 }
1085
1086 pub fn image(input: impl Into<Input>, image_task_name: impl Into<String>) -> Self {
1087 Self::new(input).image_task(image_task_name)
1088 }
1089
1090 fn new(input: impl Into<Input>) -> Self {
1091 Self {
1092 input: input.into(),
1093 input_format: None,
1094 pages: None,
1095 layer: None,
1096 text: None,
1097 font_size: None,
1098 font_width_percent: None,
1099 font_color: None,
1100 font_name: None,
1101 font_align: None,
1102 image: None,
1103 image_width: None,
1104 image_height: None,
1105 image_width_percent: None,
1106 position_vertical: None,
1107 position_horizontal: None,
1108 margin_vertical: None,
1109 margin_horizontal: None,
1110 opacity: None,
1111 rotation: None,
1112 filename: None,
1113 engine: None,
1114 engine_version: None,
1115 timeout: None,
1116 extra: BTreeMap::new(),
1117 }
1118 }
1119
1120 fn text_content(mut self, text: impl Into<String>) -> Self {
1121 self.text = Some(text.into());
1122 self
1123 }
1124
1125 fn image_task(mut self, image_task_name: impl Into<String>) -> Self {
1126 self.image = Some(image_task_name.into());
1127 self
1128 }
1129
1130 pub fn input_format(mut self, input_format: impl Into<String>) -> Self {
1131 self.input_format = Some(normalize_file_extension(input_format));
1132 self
1133 }
1134
1135 pub fn pages(mut self, pages: impl Into<String>) -> Self {
1136 self.pages = Some(pages.into());
1137 self
1138 }
1139
1140 pub fn layer(mut self, layer: Layer) -> Self {
1141 self.layer = Some(layer);
1142 self
1143 }
1144
1145 pub fn font_size(mut self, font_size: u32) -> Self {
1146 self.font_size = Some(font_size);
1147 self
1148 }
1149
1150 pub fn font_width_percent(mut self, font_width_percent: u8) -> Self {
1151 self.font_width_percent = Some(font_width_percent);
1152 self
1153 }
1154
1155 pub fn font_color(mut self, font_color: impl Into<String>) -> Self {
1156 self.font_color = Some(font_color.into());
1157 self
1158 }
1159
1160 pub fn font_name(mut self, font_name: impl Into<String>) -> Self {
1161 self.font_name = Some(font_name.into());
1162 self
1163 }
1164
1165 pub fn font_align_left(mut self) -> Self {
1166 self.font_align = Some(FontAlign::Left);
1167 self
1168 }
1169
1170 pub fn font_align_center(mut self) -> Self {
1171 self.font_align = Some(FontAlign::Center);
1172 self
1173 }
1174
1175 pub fn font_align_right(mut self) -> Self {
1176 self.font_align = Some(FontAlign::Right);
1177 self
1178 }
1179
1180 pub fn position(mut self, vertical: PositionVertical, horizontal: PositionHorizontal) -> Self {
1181 self.position_vertical = Some(vertical);
1182 self.position_horizontal = Some(horizontal);
1183 self
1184 }
1185
1186 pub fn margins(mut self, vertical: u32, horizontal: u32) -> Self {
1187 self.margin_vertical = Some(vertical);
1188 self.margin_horizontal = Some(horizontal);
1189 self
1190 }
1191
1192 pub fn opacity(mut self, opacity: u8) -> Self {
1193 self.opacity = Some(opacity);
1194 self
1195 }
1196
1197 pub fn rotation(mut self, rotation: i16) -> Self {
1198 self.rotation = Some(rotation);
1199 self
1200 }
1201
1202 pub fn image_width(mut self, image_width: u32) -> Self {
1203 self.image_width = Some(image_width);
1204 self
1205 }
1206
1207 pub fn image_height(mut self, image_height: u32) -> Self {
1208 self.image_height = Some(image_height);
1209 self
1210 }
1211
1212 pub fn image_width_percent(mut self, image_width_percent: u8) -> Self {
1213 self.image_width_percent = Some(image_width_percent);
1214 self
1215 }
1216
1217 pub fn filename(mut self, filename: impl Into<String>) -> Self {
1218 self.filename = Some(filename.into());
1219 self
1220 }
1221
1222 pub fn engine(mut self, engine: impl Into<String>) -> Self {
1223 self.engine = Some(engine.into());
1224 self
1225 }
1226
1227 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1228 self.engine_version = Some(engine_version.into());
1229 self
1230 }
1231
1232 pub fn timeout(mut self, timeout: u64) -> Self {
1233 self.timeout = Some(timeout);
1234 self
1235 }
1236
1237 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1238 self.extra.insert(key.into(), value.into());
1239 self
1240 }
1241}
1242
1243task_payload!(WatermarkTask, "watermark");
1244
1245#[derive(Clone, Debug, Serialize)]
1246pub struct CaptureWebsiteTask {
1247 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1248 extra: ExtraOptions,
1249 url: String,
1250 output_format: String,
1251 #[serde(skip_serializing_if = "Option::is_none")]
1252 engine: Option<String>,
1253 #[serde(skip_serializing_if = "Option::is_none")]
1254 engine_version: Option<String>,
1255 #[serde(skip_serializing_if = "Option::is_none")]
1256 filename: Option<String>,
1257 #[serde(skip_serializing_if = "Option::is_none")]
1258 timeout: Option<u64>,
1259}
1260
1261impl CaptureWebsiteTask {
1262 pub fn new(url: impl Into<String>, output_format: impl Into<String>) -> Self {
1263 Self {
1264 url: url.into(),
1265 output_format: normalize_file_extension(output_format),
1266 engine: None,
1267 engine_version: None,
1268 filename: None,
1269 timeout: None,
1270 extra: BTreeMap::new(),
1271 }
1272 }
1273
1274 pub fn engine(mut self, engine: impl Into<String>) -> Self {
1275 self.engine = Some(engine.into());
1276 self
1277 }
1278
1279 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1280 self.engine_version = Some(engine_version.into());
1281 self
1282 }
1283
1284 pub fn filename(mut self, filename: impl Into<String>) -> Self {
1285 self.filename = Some(filename.into());
1286 self
1287 }
1288
1289 pub fn timeout(mut self, timeout: u64) -> Self {
1290 self.timeout = Some(timeout);
1291 self
1292 }
1293
1294 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1295 self.extra.insert(key.into(), value.into());
1296 self
1297 }
1298}
1299
1300task_payload!(CaptureWebsiteTask, "capture-website");
1301
1302#[derive(Clone, Debug, Serialize)]
1303pub struct ThumbnailTask {
1304 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1305 extra: ExtraOptions,
1306 input: Input,
1307 #[serde(skip_serializing_if = "Option::is_none")]
1308 input_format: Option<String>,
1309 output_format: String,
1310 #[serde(skip_serializing_if = "Option::is_none")]
1311 engine: Option<String>,
1312 #[serde(skip_serializing_if = "Option::is_none")]
1313 engine_version: Option<String>,
1314 #[serde(skip_serializing_if = "Option::is_none")]
1315 width: Option<u32>,
1316 #[serde(skip_serializing_if = "Option::is_none")]
1317 height: Option<u32>,
1318 #[serde(skip_serializing_if = "Option::is_none")]
1319 fit: Option<String>,
1320 #[serde(skip_serializing_if = "Option::is_none")]
1321 count: Option<u32>,
1322 #[serde(skip_serializing_if = "Option::is_none")]
1323 timestamp: Option<String>,
1324 #[serde(skip_serializing_if = "Option::is_none")]
1325 filename: Option<String>,
1326 #[serde(skip_serializing_if = "Option::is_none")]
1327 timeout: Option<u64>,
1328}
1329
1330impl ThumbnailTask {
1331 pub fn new(input: impl Into<Input>, output_format: impl Into<String>) -> Self {
1332 Self {
1333 input: input.into(),
1334 input_format: None,
1335 output_format: normalize_file_extension(output_format),
1336 engine: None,
1337 engine_version: None,
1338 width: None,
1339 height: None,
1340 fit: None,
1341 count: None,
1342 timestamp: None,
1343 filename: None,
1344 timeout: None,
1345 extra: BTreeMap::new(),
1346 }
1347 }
1348
1349 pub fn input_format(mut self, input_format: impl Into<String>) -> Self {
1350 self.input_format = Some(normalize_file_extension(input_format));
1351 self
1352 }
1353
1354 pub fn engine(mut self, engine: impl Into<String>) -> Self {
1355 self.engine = Some(engine.into());
1356 self
1357 }
1358
1359 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1360 self.engine_version = Some(engine_version.into());
1361 self
1362 }
1363
1364 pub fn width(mut self, width: u32) -> Self {
1365 self.width = Some(width);
1366 self
1367 }
1368
1369 pub fn height(mut self, height: u32) -> Self {
1370 self.height = Some(height);
1371 self
1372 }
1373
1374 pub fn dimensions(mut self, width: u32, height: u32) -> Self {
1375 self.width = Some(width);
1376 self.height = Some(height);
1377 self
1378 }
1379
1380 pub fn fit(mut self, fit: impl Into<String>) -> Self {
1381 self.fit = Some(fit.into());
1382 self
1383 }
1384
1385 pub fn count(mut self, count: u32) -> Self {
1386 self.count = Some(count);
1387 self
1388 }
1389
1390 pub fn timestamp(mut self, timestamp: impl Into<String>) -> Self {
1391 self.timestamp = Some(timestamp.into());
1392 self
1393 }
1394
1395 pub fn filename(mut self, filename: impl Into<String>) -> Self {
1396 self.filename = Some(filename.into());
1397 self
1398 }
1399
1400 pub fn timeout(mut self, timeout: u64) -> Self {
1401 self.timeout = Some(timeout);
1402 self
1403 }
1404
1405 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1406 self.extra.insert(key.into(), value.into());
1407 self
1408 }
1409}
1410
1411task_payload!(ThumbnailTask, "thumbnail");
1412
1413#[derive(Clone, Debug, Serialize)]
1414pub struct MetadataTask {
1415 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1416 extra: ExtraOptions,
1417 input: Input,
1418 #[serde(skip_serializing_if = "Option::is_none")]
1419 input_format: Option<String>,
1420 #[serde(skip_serializing_if = "Option::is_none")]
1421 engine: Option<String>,
1422 #[serde(skip_serializing_if = "Option::is_none")]
1423 engine_version: Option<String>,
1424 #[serde(skip_serializing_if = "Option::is_none")]
1425 timeout: Option<u64>,
1426}
1427
1428impl MetadataTask {
1429 pub fn new(input: impl Into<Input>) -> Self {
1430 Self {
1431 input: input.into(),
1432 input_format: None,
1433 engine: None,
1434 engine_version: None,
1435 timeout: None,
1436 extra: BTreeMap::new(),
1437 }
1438 }
1439
1440 pub fn input_format(mut self, input_format: impl Into<String>) -> Self {
1441 self.input_format = Some(normalize_file_extension(input_format));
1442 self
1443 }
1444
1445 pub fn engine(mut self, engine: impl Into<String>) -> Self {
1446 self.engine = Some(engine.into());
1447 self
1448 }
1449
1450 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1451 self.engine_version = Some(engine_version.into());
1452 self
1453 }
1454
1455 pub fn timeout(mut self, timeout: u64) -> Self {
1456 self.timeout = Some(timeout);
1457 self
1458 }
1459
1460 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1461 self.extra.insert(key.into(), value.into());
1462 self
1463 }
1464}
1465
1466task_payload!(MetadataTask, "metadata");
1467
1468#[derive(Clone, Debug, Serialize)]
1469pub struct MetadataWriteTask {
1470 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1471 extra: ExtraOptions,
1472 input: Input,
1473 #[serde(skip_serializing_if = "Option::is_none")]
1474 input_format: Option<String>,
1475 #[serde(skip_serializing_if = "Option::is_none")]
1476 engine: Option<String>,
1477 #[serde(skip_serializing_if = "Option::is_none")]
1478 engine_version: Option<String>,
1479 metadata: BTreeMap<String, Value>,
1480 #[serde(skip_serializing_if = "Option::is_none")]
1481 filename: Option<String>,
1482 #[serde(skip_serializing_if = "Option::is_none")]
1483 timeout: Option<u64>,
1484}
1485
1486impl MetadataWriteTask {
1487 pub fn new(input: impl Into<Input>) -> Self {
1488 Self {
1489 input: input.into(),
1490 input_format: None,
1491 engine: None,
1492 engine_version: None,
1493 metadata: BTreeMap::new(),
1494 filename: None,
1495 timeout: None,
1496 extra: BTreeMap::new(),
1497 }
1498 }
1499
1500 pub fn input_format(mut self, input_format: impl Into<String>) -> Self {
1501 self.input_format = Some(normalize_file_extension(input_format));
1502 self
1503 }
1504
1505 pub fn engine(mut self, engine: impl Into<String>) -> Self {
1506 self.engine = Some(engine.into());
1507 self
1508 }
1509
1510 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1511 self.engine_version = Some(engine_version.into());
1512 self
1513 }
1514
1515 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1516 self.metadata.insert(key.into(), value.into());
1517 self
1518 }
1519
1520 pub fn metadata_map(mut self, metadata: BTreeMap<String, Value>) -> Self {
1521 self.metadata = metadata;
1522 self
1523 }
1524
1525 pub fn filename(mut self, filename: impl Into<String>) -> Self {
1526 self.filename = Some(filename.into());
1527 self
1528 }
1529
1530 pub fn timeout(mut self, timeout: u64) -> Self {
1531 self.timeout = Some(timeout);
1532 self
1533 }
1534
1535 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1536 self.extra.insert(key.into(), value.into());
1537 self
1538 }
1539}
1540
1541task_payload!(MetadataWriteTask, "metadata/write");
1542
1543#[derive(Clone, Debug, Serialize)]
1544pub struct MergeTask {
1545 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1546 extra: ExtraOptions,
1547 input: Input,
1548 output_format: String,
1549 #[serde(skip_serializing_if = "Option::is_none")]
1550 engine: Option<String>,
1551 #[serde(skip_serializing_if = "Option::is_none")]
1552 engine_version: Option<String>,
1553 #[serde(skip_serializing_if = "Option::is_none")]
1554 filename: Option<String>,
1555 #[serde(skip_serializing_if = "Option::is_none")]
1556 timeout: Option<u64>,
1557}
1558
1559impl MergeTask {
1560 pub fn new(input: impl Into<Input>, output_format: impl Into<String>) -> Self {
1561 Self {
1562 input: input.into(),
1563 output_format: normalize_file_extension(output_format),
1564 engine: None,
1565 engine_version: None,
1566 filename: None,
1567 timeout: None,
1568 extra: BTreeMap::new(),
1569 }
1570 }
1571
1572 pub fn engine(mut self, engine: impl Into<String>) -> Self {
1573 self.engine = Some(engine.into());
1574 self
1575 }
1576
1577 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1578 self.engine_version = Some(engine_version.into());
1579 self
1580 }
1581
1582 pub fn filename(mut self, filename: impl Into<String>) -> Self {
1583 self.filename = Some(filename.into());
1584 self
1585 }
1586
1587 pub fn timeout(mut self, timeout: u64) -> Self {
1588 self.timeout = Some(timeout);
1589 self
1590 }
1591
1592 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1593 self.extra.insert(key.into(), value.into());
1594 self
1595 }
1596}
1597
1598task_payload!(MergeTask, "merge");
1599
1600#[derive(Clone, Debug, Serialize)]
1601pub struct ArchiveTask {
1602 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1603 extra: ExtraOptions,
1604 input: Input,
1605 output_format: String,
1606 #[serde(skip_serializing_if = "Option::is_none")]
1607 engine: Option<String>,
1608 #[serde(skip_serializing_if = "Option::is_none")]
1609 engine_version: Option<String>,
1610 #[serde(skip_serializing_if = "Option::is_none")]
1611 filename: Option<String>,
1612 #[serde(skip_serializing_if = "Option::is_none")]
1613 timeout: Option<u64>,
1614}
1615
1616impl ArchiveTask {
1617 pub fn new(input: impl Into<Input>, output_format: impl Into<String>) -> Self {
1618 Self {
1619 input: input.into(),
1620 output_format: normalize_file_extension(output_format),
1621 engine: None,
1622 engine_version: None,
1623 filename: None,
1624 timeout: None,
1625 extra: BTreeMap::new(),
1626 }
1627 }
1628
1629 pub fn engine(mut self, engine: impl Into<String>) -> Self {
1630 self.engine = Some(engine.into());
1631 self
1632 }
1633
1634 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1635 self.engine_version = Some(engine_version.into());
1636 self
1637 }
1638
1639 pub fn filename(mut self, filename: impl Into<String>) -> Self {
1640 self.filename = Some(filename.into());
1641 self
1642 }
1643
1644 pub fn timeout(mut self, timeout: u64) -> Self {
1645 self.timeout = Some(timeout);
1646 self
1647 }
1648
1649 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1650 self.extra.insert(key.into(), value.into());
1651 self
1652 }
1653}
1654
1655task_payload!(ArchiveTask, "archive");
1656
1657#[derive(Clone, Debug, Serialize)]
1658pub struct CommandTask {
1659 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1660 extra: ExtraOptions,
1661 input: Input,
1662 engine: String,
1663 command: String,
1664 arguments: String,
1665 #[serde(skip_serializing_if = "Option::is_none")]
1666 engine_version: Option<String>,
1667 #[serde(skip_serializing_if = "Option::is_none")]
1668 capture_output: Option<bool>,
1669 #[serde(skip_serializing_if = "Option::is_none")]
1670 timeout: Option<u64>,
1671}
1672
1673impl CommandTask {
1674 pub fn new(
1675 input: impl Into<Input>,
1676 engine: impl Into<String>,
1677 command: impl Into<String>,
1678 arguments: impl Into<String>,
1679 ) -> Self {
1680 Self {
1681 input: input.into(),
1682 engine: engine.into(),
1683 command: command.into(),
1684 arguments: arguments.into(),
1685 engine_version: None,
1686 capture_output: None,
1687 timeout: None,
1688 extra: BTreeMap::new(),
1689 }
1690 }
1691
1692 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1693 self.engine_version = Some(engine_version.into());
1694 self
1695 }
1696
1697 pub fn capture_output(mut self, capture_output: bool) -> Self {
1698 self.capture_output = Some(capture_output);
1699 self
1700 }
1701
1702 pub fn timeout(mut self, timeout: u64) -> Self {
1703 self.timeout = Some(timeout);
1704 self
1705 }
1706
1707 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1708 self.extra.insert(key.into(), value.into());
1709 self
1710 }
1711}
1712
1713task_payload!(CommandTask, "command");
1714
1715macro_rules! pdf_task {
1716 ($type:ident, $operation:literal) => {
1717 #[derive(Clone, Debug, Serialize)]
1718 pub struct $type {
1719 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1720 extra: ExtraOptions,
1721 input: Input,
1722 #[serde(skip_serializing_if = "Option::is_none")]
1723 engine: Option<String>,
1724 #[serde(skip_serializing_if = "Option::is_none")]
1725 engine_version: Option<String>,
1726 #[serde(skip_serializing_if = "Option::is_none")]
1727 filename: Option<String>,
1728 #[serde(skip_serializing_if = "Option::is_none")]
1729 timeout: Option<u64>,
1730 }
1731
1732 impl $type {
1733 pub fn new(input: impl Into<Input>) -> Self {
1734 Self {
1735 input: input.into(),
1736 engine: None,
1737 engine_version: None,
1738 filename: None,
1739 timeout: None,
1740 extra: BTreeMap::new(),
1741 }
1742 }
1743
1744 pub fn engine(mut self, engine: impl Into<String>) -> Self {
1745 self.engine = Some(engine.into());
1746 self
1747 }
1748
1749 pub fn engine_version(mut self, engine_version: impl Into<String>) -> Self {
1750 self.engine_version = Some(engine_version.into());
1751 self
1752 }
1753
1754 pub fn filename(mut self, filename: impl Into<String>) -> Self {
1755 self.filename = Some(filename.into());
1756 self
1757 }
1758
1759 pub fn timeout(mut self, timeout: u64) -> Self {
1760 self.timeout = Some(timeout);
1761 self
1762 }
1763
1764 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1765 self.extra.insert(key.into(), value.into());
1766 self
1767 }
1768 }
1769
1770 task_payload!($type, $operation);
1771 };
1772}
1773
1774pdf_task!(PdfATask, "pdf/a");
1775pdf_task!(PdfXTask, "pdf/x");
1776pdf_task!(PdfOcrTask, "pdf/ocr");
1777pdf_task!(PdfEncryptTask, "pdf/encrypt");
1778pdf_task!(PdfDecryptTask, "pdf/decrypt");
1779pdf_task!(PdfSplitPagesTask, "pdf/split-pages");
1780pdf_task!(PdfExtractPagesTask, "pdf/extract-pages");
1781pdf_task!(PdfRotatePagesTask, "pdf/rotate-pages");
1782
1783#[derive(Clone, Debug, Serialize)]
1784pub struct ExportUrlTask {
1785 input: Input,
1786 #[serde(skip_serializing_if = "Option::is_none")]
1787 inline: Option<bool>,
1788 #[serde(skip_serializing_if = "Option::is_none")]
1789 archive_multiple_files: Option<bool>,
1790}
1791
1792impl ExportUrlTask {
1793 pub fn new(input: impl Into<Input>) -> Self {
1794 Self {
1795 input: input.into(),
1796 inline: None,
1797 archive_multiple_files: None,
1798 }
1799 }
1800
1801 pub fn inline(mut self, inline: bool) -> Self {
1802 self.inline = Some(inline);
1803 self
1804 }
1805
1806 pub fn archive_multiple_files(mut self, archive_multiple_files: bool) -> Self {
1807 self.archive_multiple_files = Some(archive_multiple_files);
1808 self
1809 }
1810}
1811
1812task_payload!(ExportUrlTask, "export/url");
1813
1814#[derive(Clone, Serialize)]
1815pub struct S3ExportTask {
1816 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1817 extra: ExtraOptions,
1818 input: Input,
1819 bucket: String,
1820 region: String,
1821 #[serde(skip_serializing_if = "Option::is_none")]
1822 endpoint: Option<String>,
1823 #[serde(skip_serializing_if = "Option::is_none")]
1824 key: Option<String>,
1825 #[serde(skip_serializing_if = "Option::is_none")]
1826 key_prefix: Option<String>,
1827 access_key_id: String,
1828 secret_access_key: String,
1829 #[serde(skip_serializing_if = "Option::is_none")]
1830 session_token: Option<String>,
1831}
1832
1833impl S3ExportTask {
1834 pub fn new(
1835 input: impl Into<Input>,
1836 bucket: impl Into<String>,
1837 region: impl Into<String>,
1838 access_key_id: impl Into<String>,
1839 secret_access_key: impl Into<String>,
1840 ) -> Self {
1841 Self {
1842 input: input.into(),
1843 bucket: bucket.into(),
1844 region: region.into(),
1845 endpoint: None,
1846 key: None,
1847 key_prefix: None,
1848 access_key_id: access_key_id.into(),
1849 secret_access_key: secret_access_key.into(),
1850 session_token: None,
1851 extra: BTreeMap::new(),
1852 }
1853 }
1854
1855 pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
1856 self.endpoint = Some(endpoint.into());
1857 self
1858 }
1859
1860 pub fn key(mut self, key: impl Into<String>) -> Self {
1861 self.key = Some(key.into());
1862 self
1863 }
1864
1865 pub fn key_prefix(mut self, key_prefix: impl Into<String>) -> Self {
1866 self.key_prefix = Some(key_prefix.into());
1867 self
1868 }
1869
1870 pub fn session_token(mut self, session_token: impl Into<String>) -> Self {
1871 self.session_token = Some(session_token.into());
1872 self
1873 }
1874
1875 pub fn acl(self, acl: impl Into<String>) -> Self {
1876 self.option("acl", acl.into())
1877 }
1878
1879 pub fn cache_control(self, cache_control: impl Into<String>) -> Self {
1880 self.option("cache_control", cache_control.into())
1881 }
1882
1883 pub fn content_disposition(self, content_disposition: impl Into<String>) -> Self {
1884 self.option("content_disposition", content_disposition.into())
1885 }
1886
1887 pub fn content_type(self, content_type: impl Into<String>) -> Self {
1888 self.option("content_type", content_type.into())
1889 }
1890
1891 pub fn server_side_encryption(self, server_side_encryption: impl Into<String>) -> Self {
1892 self.option("server_side_encryption", server_side_encryption.into())
1893 }
1894
1895 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1896 insert_extra_object_field(&mut self.extra, "metadata", key, value);
1897 self
1898 }
1899
1900 pub fn tag(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1901 insert_extra_object_field(&mut self.extra, "tagging", key, value);
1902 self
1903 }
1904
1905 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1906 self.extra.insert(key.into(), value.into());
1907 self
1908 }
1909}
1910
1911task_payload!(S3ExportTask, "export/s3");
1912
1913#[derive(Clone, Serialize)]
1914pub struct AzureBlobExportTask {
1915 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1916 extra: ExtraOptions,
1917 input: Input,
1918 storage_account: String,
1919 #[serde(skip_serializing_if = "Option::is_none")]
1920 storage_access_key: Option<String>,
1921 #[serde(skip_serializing_if = "Option::is_none")]
1922 sas_token: Option<String>,
1923 container: String,
1924 #[serde(skip_serializing_if = "Option::is_none")]
1925 blob: Option<String>,
1926 #[serde(skip_serializing_if = "Option::is_none")]
1927 blob_prefix: Option<String>,
1928}
1929
1930impl AzureBlobExportTask {
1931 pub fn new(
1932 input: impl Into<Input>,
1933 storage_account: impl Into<String>,
1934 container: impl Into<String>,
1935 ) -> Self {
1936 Self {
1937 input: input.into(),
1938 storage_account: storage_account.into(),
1939 storage_access_key: None,
1940 sas_token: None,
1941 container: container.into(),
1942 blob: None,
1943 blob_prefix: None,
1944 extra: BTreeMap::new(),
1945 }
1946 }
1947
1948 pub fn storage_access_key(mut self, storage_access_key: impl Into<String>) -> Self {
1949 self.storage_access_key = Some(storage_access_key.into());
1950 self
1951 }
1952
1953 pub fn sas_token(mut self, sas_token: impl Into<String>) -> Self {
1954 self.sas_token = Some(sas_token.into());
1955 self
1956 }
1957
1958 pub fn blob(mut self, blob: impl Into<String>) -> Self {
1959 self.blob = Some(blob.into());
1960 self
1961 }
1962
1963 pub fn blob_prefix(mut self, blob_prefix: impl Into<String>) -> Self {
1964 self.blob_prefix = Some(blob_prefix.into());
1965 self
1966 }
1967
1968 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1969 insert_extra_object_field(&mut self.extra, "metadata", key, value);
1970 self
1971 }
1972
1973 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
1974 self.extra.insert(key.into(), value.into());
1975 self
1976 }
1977}
1978
1979task_payload!(AzureBlobExportTask, "export/azure/blob");
1980
1981#[derive(Clone, Serialize)]
1982pub struct GoogleCloudStorageExportTask {
1983 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
1984 extra: ExtraOptions,
1985 input: Input,
1986 project_id: String,
1987 bucket: String,
1988 client_email: String,
1989 private_key: String,
1990 #[serde(skip_serializing_if = "Option::is_none")]
1991 file: Option<String>,
1992 #[serde(skip_serializing_if = "Option::is_none")]
1993 file_prefix: Option<String>,
1994}
1995
1996impl GoogleCloudStorageExportTask {
1997 pub fn new(
1998 input: impl Into<Input>,
1999 project_id: impl Into<String>,
2000 bucket: impl Into<String>,
2001 client_email: impl Into<String>,
2002 private_key: impl Into<String>,
2003 ) -> Self {
2004 Self {
2005 input: input.into(),
2006 project_id: project_id.into(),
2007 bucket: bucket.into(),
2008 client_email: client_email.into(),
2009 private_key: private_key.into(),
2010 file: None,
2011 file_prefix: None,
2012 extra: BTreeMap::new(),
2013 }
2014 }
2015
2016 pub fn file(mut self, file: impl Into<String>) -> Self {
2017 self.file = Some(file.into());
2018 self
2019 }
2020
2021 pub fn file_prefix(mut self, file_prefix: impl Into<String>) -> Self {
2022 self.file_prefix = Some(file_prefix.into());
2023 self
2024 }
2025
2026 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
2027 self.extra.insert(key.into(), value.into());
2028 self
2029 }
2030}
2031
2032task_payload!(GoogleCloudStorageExportTask, "export/google-cloud-storage");
2033
2034#[derive(Clone, Serialize)]
2035pub struct OpenStackExportTask {
2036 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
2037 extra: ExtraOptions,
2038 input: Input,
2039 auth_url: String,
2040 username: String,
2041 password: String,
2042 region: String,
2043 container: String,
2044 #[serde(skip_serializing_if = "Option::is_none")]
2045 file: Option<String>,
2046 #[serde(skip_serializing_if = "Option::is_none")]
2047 file_prefix: Option<String>,
2048}
2049
2050impl OpenStackExportTask {
2051 pub fn new(
2052 input: impl Into<Input>,
2053 auth_url: impl Into<String>,
2054 username: impl Into<String>,
2055 password: impl Into<String>,
2056 region: impl Into<String>,
2057 container: impl Into<String>,
2058 ) -> Self {
2059 Self {
2060 input: input.into(),
2061 auth_url: auth_url.into(),
2062 username: username.into(),
2063 password: password.into(),
2064 region: region.into(),
2065 container: container.into(),
2066 file: None,
2067 file_prefix: None,
2068 extra: BTreeMap::new(),
2069 }
2070 }
2071
2072 pub fn file(mut self, file: impl Into<String>) -> Self {
2073 self.file = Some(file.into());
2074 self
2075 }
2076
2077 pub fn file_prefix(mut self, file_prefix: impl Into<String>) -> Self {
2078 self.file_prefix = Some(file_prefix.into());
2079 self
2080 }
2081
2082 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
2083 self.extra.insert(key.into(), value.into());
2084 self
2085 }
2086}
2087
2088task_payload!(OpenStackExportTask, "export/openstack");
2089
2090#[derive(Clone, Serialize)]
2091pub struct SftpExportTask {
2092 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
2093 extra: ExtraOptions,
2094 input: Input,
2095 host: String,
2096 #[serde(skip_serializing_if = "Option::is_none")]
2097 port: Option<u16>,
2098 username: String,
2099 #[serde(skip_serializing_if = "Option::is_none")]
2100 password: Option<String>,
2101 #[serde(skip_serializing_if = "Option::is_none")]
2102 private_key: Option<String>,
2103 #[serde(skip_serializing_if = "Option::is_none")]
2104 file: Option<String>,
2105 #[serde(skip_serializing_if = "Option::is_none")]
2106 path: Option<String>,
2107}
2108
2109impl SftpExportTask {
2110 pub fn new(
2111 input: impl Into<Input>,
2112 host: impl Into<String>,
2113 username: impl Into<String>,
2114 ) -> Self {
2115 Self {
2116 input: input.into(),
2117 host: host.into(),
2118 port: None,
2119 username: username.into(),
2120 password: None,
2121 private_key: None,
2122 file: None,
2123 path: None,
2124 extra: BTreeMap::new(),
2125 }
2126 }
2127
2128 pub fn port(mut self, port: u16) -> Self {
2129 self.port = Some(port);
2130 self
2131 }
2132
2133 pub fn password(mut self, password: impl Into<String>) -> Self {
2134 self.password = Some(password.into());
2135 self
2136 }
2137
2138 pub fn private_key(mut self, private_key: impl Into<String>) -> Self {
2139 self.private_key = Some(private_key.into());
2140 self
2141 }
2142
2143 pub fn file(mut self, file: impl Into<String>) -> Self {
2144 self.file = Some(file.into());
2145 self
2146 }
2147
2148 pub fn path(mut self, path: impl Into<String>) -> Self {
2149 self.path = Some(path.into());
2150 self
2151 }
2152
2153 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
2154 self.extra.insert(key.into(), value.into());
2155 self
2156 }
2157}
2158
2159task_payload!(SftpExportTask, "export/sftp");
2160
2161#[derive(Clone, Serialize)]
2162pub struct ExportUploadTask {
2163 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
2164 extra: ExtraOptions,
2165 input: Input,
2166 url: String,
2167 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
2168 headers: BTreeMap<String, String>,
2169}
2170
2171impl ExportUploadTask {
2172 pub fn new(input: impl Into<Input>, url: impl Into<String>) -> Self {
2173 Self {
2174 input: input.into(),
2175 url: url.into(),
2176 headers: BTreeMap::new(),
2177 extra: BTreeMap::new(),
2178 }
2179 }
2180
2181 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
2182 self.headers.insert(key.into(), value.into());
2183 self
2184 }
2185
2186 pub fn option(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
2187 self.extra.insert(key.into(), value.into());
2188 self
2189 }
2190}
2191
2192task_payload!(ExportUploadTask, "export/upload");
2193
2194#[derive(Clone)]
2209pub struct GenericTask {
2210 operation: String,
2211 data: Map<String, Value>,
2212}
2213
2214impl GenericTask {
2215 pub fn new(operation: impl Into<String>) -> Self {
2217 Self {
2218 operation: operation.into(),
2219 data: Map::new(),
2220 }
2221 }
2222
2223 pub fn field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
2225 self.data.insert(key.into(), value.into());
2226 self
2227 }
2228
2229 pub fn operation(&self) -> &str {
2231 self.operation.as_str()
2232 }
2233
2234 pub fn data(&self) -> &Map<String, Value> {
2236 &self.data
2237 }
2238}
2239
2240impl From<GenericTask> for TaskRequest {
2241 fn from(value: GenericTask) -> Self {
2242 Self {
2243 operation: value.operation,
2244 payload: value.data,
2245 }
2246 }
2247}
2248
2249impl fmt::Debug for GenericTask {
2250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2251 f.debug_struct("GenericTask")
2252 .field("operation", &self.operation)
2253 .field("data", &"REDACTED")
2254 .finish()
2255 }
2256}
2257
2258debug_struct_redacted!(ImportUrlTask {
2259 fields: [url, filename],
2260 redacted: [],
2261 redacted_options: [],
2262 redacted_maps: [headers]
2263});
2264
2265debug_struct_redacted!(S3ImportTask {
2266 fields: [bucket, region, endpoint, key, key_prefix, filename],
2267 redacted: [access_key_id, secret_access_key],
2268 redacted_options: [session_token],
2269 redacted_maps: []
2270});
2271
2272debug_struct_redacted!(AzureBlobImportTask {
2273 fields: [storage_account, container, blob, blob_prefix, filename],
2274 redacted: [],
2275 redacted_options: [storage_access_key, sas_token],
2276 redacted_maps: []
2277});
2278
2279debug_struct_redacted!(GoogleCloudStorageImportTask {
2280 fields: [
2281 project_id,
2282 bucket,
2283 client_email,
2284 file,
2285 file_prefix,
2286 filename
2287 ],
2288 redacted: [private_key],
2289 redacted_options: [],
2290 redacted_maps: []
2291});
2292
2293debug_struct_redacted!(OpenStackImportTask {
2294 fields: [
2295 auth_url,
2296 username,
2297 region,
2298 container,
2299 file,
2300 file_prefix,
2301 filename
2302 ],
2303 redacted: [password],
2304 redacted_options: [],
2305 redacted_maps: []
2306});
2307
2308debug_struct_redacted!(SftpImportTask {
2309 fields: [host, port, username, file, path, filename],
2310 redacted: [],
2311 redacted_options: [password, private_key],
2312 redacted_maps: []
2313});
2314
2315debug_struct_redacted!(S3ExportTask {
2316 fields: [input, bucket, region, endpoint, key, key_prefix, extra],
2317 redacted: [access_key_id, secret_access_key],
2318 redacted_options: [session_token],
2319 redacted_maps: []
2320});
2321
2322debug_struct_redacted!(AzureBlobExportTask {
2323 fields: [input, storage_account, container, blob, blob_prefix, extra],
2324 redacted: [],
2325 redacted_options: [storage_access_key, sas_token],
2326 redacted_maps: []
2327});
2328
2329debug_struct_redacted!(GoogleCloudStorageExportTask {
2330 fields: [
2331 input,
2332 project_id,
2333 bucket,
2334 client_email,
2335 file,
2336 file_prefix,
2337 extra
2338 ],
2339 redacted: [private_key],
2340 redacted_options: [],
2341 redacted_maps: []
2342});
2343
2344debug_struct_redacted!(OpenStackExportTask {
2345 fields: [
2346 input,
2347 auth_url,
2348 username,
2349 region,
2350 container,
2351 file,
2352 file_prefix,
2353 extra
2354 ],
2355 redacted: [password],
2356 redacted_options: [],
2357 redacted_maps: []
2358});
2359
2360debug_struct_redacted!(SftpExportTask {
2361 fields: [input, host, port, username, file, path, extra],
2362 redacted: [],
2363 redacted_options: [password, private_key],
2364 redacted_maps: []
2365});
2366
2367debug_struct_redacted!(ExportUploadTask {
2368 fields: [input, url, extra],
2369 redacted: [],
2370 redacted_options: [],
2371 redacted_maps: [headers]
2372});