1use std::borrow::Cow;
5use std::collections::HashMap;
6
7use serde::{Deserialize, Serialize};
8
9use super::Format;
10
11#[derive(Debug)]
15pub enum Input<'a, 'b> {
16 Single(Cow<'a, str>),
17 List(Cow<'b, [Cow<'a, str>]>),
18}
19
20impl<'a, 'b> Input<'a, 'b> {
21 pub fn into_static(self) -> Input<'static, 'static> {
23 match self {
24 Input::Single(Cow::Owned(s)) => Input::Single(Cow::Owned(s)),
25 Input::Single(Cow::Borrowed(s)) => Input::Single(Cow::Owned(s.to_string())),
26 Input::List(Cow::Owned(items)) => Input::List(Cow::Owned(
27 items
28 .into_iter()
29 .map(Cow::into_owned)
30 .map(Cow::Owned)
31 .collect(),
32 )),
33 Input::List(Cow::Borrowed(items)) => Input::List(Cow::Owned(
34 items
35 .iter()
36 .map(std::ops::Deref::deref)
37 .map(str::to_string)
38 .map(Cow::Owned)
39 .collect(),
40 )),
41 }
42 }
43}
44
45impl<'a, 'b> Serialize for Input<'a, 'b> {
46 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
47 match self {
48 Input::Single(item) => serializer.serialize_str(item),
49 Input::List(items) => Serialize::serialize(items, serializer),
50 }
51 }
52}
53
54impl<'a, 'b> From<&'a str> for Input<'a, 'b> {
55 fn from(s: &'a str) -> Input<'a, 'b> {
56 Input::Single(Cow::Borrowed(s))
57 }
58}
59
60impl<'a, 'b> From<String> for Input<'a, 'b> {
61 fn from(s: String) -> Input<'a, 'b> {
62 Input::Single(Cow::Owned(s))
63 }
64}
65
66impl<'a, 'b, T: AsRef<str>> From<&'a T> for Input<'a, 'b> {
67 fn from(s: &'a T) -> Input<'a, 'b> {
68 Input::Single(Cow::Borrowed(s.as_ref()))
69 }
70}
71
72impl<'a, 'b> From<Vec<String>> for Input<'a, 'b> {
73 fn from(items: Vec<String>) -> Input<'a, 'b> {
74 Input::List(Cow::Owned(items.into_iter().map(Cow::Owned).collect()))
75 }
76}
77
78impl<'a, 'b> From<&'a [String]> for Input<'a, 'b> {
79 fn from(items: &'a [String]) -> Input<'a, 'b> {
80 Input::List(Cow::Owned(
81 items
82 .iter()
83 .map(std::ops::Deref::deref)
84 .map(Cow::Borrowed)
85 .collect(),
86 ))
87 }
88}
89
90impl<'a, 'b, 'c: 'a> From<&'c [&'a str]> for Input<'a, 'b> {
91 fn from(items: &'c [&'a str]) -> Input<'a, 'b> {
92 Input::List(Cow::Owned(
93 items.iter().map(|item| Cow::Borrowed(*item)).collect(),
94 ))
95 }
96}
97
98impl<'a, 'b> From<Vec<Cow<'a, str>>> for Input<'a, 'b> {
99 fn from(items: Vec<Cow<'a, str>>) -> Input<'a, 'b> {
100 Input::List(Cow::Owned(items))
101 }
102}
103
104impl<'a, 'b> From<&'b [Cow<'a, str>]> for Input<'a, 'b> {
105 fn from(items: &'b [Cow<'a, str>]) -> Input<'a, 'b> {
106 Input::List(Cow::Borrowed(items))
107 }
108}
109
110macro_rules! make_task_types {
111 (__field_type opt: $field_type:ty) => {
115 Option<$field_type>
116 };
117 (__field_type req: $field_type:ty) => {
121 $field_type
122 };
123
124 (
125 $(#[$task_enum_meta:meta])*
126 $enum_vis:vis enum $Task:ident<'a>;
127
128 $(
129 $(#[$task_meta:meta])*
130 $struct_vis:vis struct $TaskName:ident<'a> {
131 operation: $operation:expr,
132 $(
133 $(#[$req_field_meta:meta])*
134 req $req_field_name:ident: $req_field_type:ty,
135 )*
136 $(
137 opt $opt_field_name:ident: $opt_field_type:ty,
138 )*
139 }
140 )*
147 ) => {
148 $(#[$task_enum_meta])*
149 $enum_vis enum $Task<'a> {
150 $(
151 $TaskName($TaskName<'a>),
152 )*
153 }
154
155 impl<'a> $Task<'a> {
156 pub fn operation(&self) -> &'static str {
158 match self {
159 $($Task::$TaskName(_) => $operation,)*
160 }
161 }
162
163 pub fn to_job_task(&self) -> serde_json::Result<serde_json::Value> {
168 match self {
169 $($Task::$TaskName(task) => task.to_job_task(),)*
170 }
171 }
172 }
173
174 $(
175 $(#[$task_meta])*
176 #[derive(serde::Serialize, Debug)]
177 $struct_vis struct $TaskName<'a> {
178 $(
179 $(#[$req_field_meta])*
180 $struct_vis $req_field_name: make_task_types!(__field_type req: $req_field_type),
181 )*
182 $(
183 #[serde(skip_serializing_if = "Option::is_none")]
185 $struct_vis $opt_field_name: make_task_types!(__field_type opt: $opt_field_type),
186 )*
187 }
188
189 hapic::json_api_call!(json <'a> ($crate::ApiCall) $operation: $TaskName<'a> as $TaskName<'a> => TasksOutput as Status);
198
199 impl<'a> From<$TaskName<'a>> for $Task<'a> {
200 fn from(task: $TaskName<'a>) -> $Task<'a> {
201 $Task::$TaskName(task)
202 }
203 }
204
205 impl<'a> $TaskName<'a> {
206 pub fn to_job_task(&self) -> serde_json::Result<serde_json::Value> {
211 let task = self;
212 let mut value = serde_json::to_value(task)?;
217 match &mut value {
218 serde_json::Value::Object(value) => {
219 value.insert(
220 "operation".to_string(),
221 serde_json::Value::String($operation.to_string()),
222 );
223 },
224 _ => unreachable!(),
225 }
226 Ok(value)
227 }
228 }
229 )*
230 }
231}
232
233impl<'a> Task<'a> {
234 pub fn task_uri(&self, endpoint: &str) -> String {
235 format!("{endpoint}/{}", self.operation())
236 }
237}
238
239make_task_types!(
240 pub enum Task<'a>;
242
243 pub struct ImportUrl<'a> {
247 operation: "import/url",
248
249 req url: Cow<'a, str>,
250 opt filename: Cow<'a, str>,
251 opt headers: HashMap<String, String>,
252 }
253
254 pub struct ImportS3<'a> {
258 operation: "import/s3",
259
260 req bucket: Cow<'a, str>,
261 req region: Cow<'a, str>,
262 req access_key_id: Cow<'a, str>,
263 req secret_access_key: Cow<'a, str>,
264 opt endpoint: Cow<'a, str>,
265 opt key: Cow<'a, str>,
266 opt key_prefix: Cow<'a, str>,
267 opt session_token: Cow<'a, str>,
268 opt filename: Cow<'a, str>,
269 }
270
271 pub struct ImportAzureBlob<'a> {
275 operation: "import/azure/blob",
276
277 req storage_account: Cow<'a, str>,
278 req container: Cow<'a, str>,
279 opt storage_access_key: Cow<'a, str>,
280 opt sas_token: Cow<'a, str>,
281 opt blob: Cow<'a, str>,
282 opt blob_prefix: Cow<'a, str>,
283 opt filename: Cow<'a, str>,
284 }
285
286 pub struct ImportGoogleCloud<'a> {
290 operation: "import/google-cloud-storage",
291
292 req project_id: Cow<'a, str>,
293 req bucket: Cow<'a, str>,
294 req client_email: Cow<'a, str>,
295 req private_key: Cow<'a, str>,
296 opt file: Cow<'a, str>,
297 opt file_prefix: Cow<'a, str>,
298 opt filename: Cow<'a, str>,
299 }
300
301 pub struct ImportOpenStack<'a> {
305 operation: "import/openstack",
306
307 req auth_url: Cow<'a, str>,
308 req username: Cow<'a, str>,
309 req password: Cow<'a, str>,
310 req region: Cow<'a, str>,
311 req container: Cow<'a, str>,
312 opt tenant_name: Cow<'a, str>,
313 opt file: Cow<'a, str>,
314 opt file_prefix: Cow<'a, str>,
315 opt filename: Cow<'a, str>,
316 }
317
318 pub struct ImportSFTP<'a> {
322 operation: "import/sftp",
323
324 req host: Cow<'a, str>,
325 req username: Cow<'a, str>,
326 req password: Cow<'a, str>,
327 opt port: u16,
328 opt private_key: Cow<'a, str>,
329 opt file: Cow<'a, str>,
330 opt path: Cow<'a, str>,
331 opt filename: Cow<'a, str>,
332 }
333
334 pub struct Convert<'a> {
341 operation: "convert",
342
343 req input: Input<'a, 'a>,
347 req output_format: Format,
348 opt input_format: Format,
349 opt filename: Cow<'a, str>,
350 opt engine: Cow<'a, str>,
351 opt engine_version: Cow<'a, str>,
352 opt timeout: u32,
353 }
354
355 pub struct Optimize<'a> {
359 operation: "optimize",
360
361 req input: Input<'a, 'a>,
365 opt input_format: Format,
366 opt profile: OptimizationProfile,
367 opt flatten_signatures: bool,
368 opt colorspace: Colorspace,
369 opt filename: Cow<'a, str>,
370 opt engine: Cow<'a, str>,
371 opt engine_version: Cow<'a, str>,
372 opt timeout: u32,
373 }
374
375 pub struct Watermark<'a> {
379 operation: "watermark",
380
381 req input: Input<'a, 'a>,
385 opt input_format: Format,
386 opt pages: Cow<'a, str>,
387 opt layer: WatermarkLayer,
388 opt text: Cow<'a, str>,
389 opt font_size: u16,
390 opt font_width_percent: u8,
391 opt font_color: Cow<'a, str>,
392 opt font_name: Cow<'a, str>,
393 opt font_align: FontAlign,
394 opt image: Cow<'a, str>,
395 opt image_width: u32,
396 opt image_width_percent: u8,
397 opt position_vertical: VerticalPosition,
398 opt position_horizontal: HorizontalPosition,
399 opt margin_vertical: u32,
400 opt opacity: u8,
401 opt rotation: u16,
402 opt filename: Cow<'a, str>,
403 opt engine: Cow<'a, str>,
404 opt engine_version: Cow<'a, str>,
405 opt timeout: u32,
406 }
407
408 pub struct Thumbnail<'a> {
412 operation: "thumbnail",
413
414 req input: Cow<'a, str>,
418 req output_format: Format,
420 opt input_format: Format,
421 opt width: u32,
422 opt height: u32,
423 opt fit: ThumbnailFit,
424 opt count: u32,
425 opt timestamp: Cow<'a, str>,
426 opt filename: Cow<'a, str>,
427 opt engine: Cow<'a, str>,
428 opt engine_version: Cow<'a, str>,
429 opt timeout: u32,
430 }
431
432 pub struct Merge<'a> {
436 operation: "merge",
437
438 req input: Input<'a, 'a>,
442 req output_format: Format,
444 opt filename: Cow<'a, str>,
445 opt engine: Cow<'a, str>,
446 opt engine_version: Cow<'a, str>,
447 opt timeout: u32,
448 }
449
450 pub struct Archive<'a> {
454 operation: "archive",
455
456 req input: Input<'a, 'a>,
460 req output_format: Format,
462 opt filename: Cow<'a, str>,
463 opt engine: Cow<'a, str>,
464 opt engine_version: Cow<'a, str>,
465 opt timeout: u32,
466 }
467
468 pub struct Capture<'a> {
472 operation: "capture-website",
473
474 req url: Cow<'a, str>,
476 req output_format: Format,
478 req print_background: bool,
479 req display_header_footer: bool,
480 #[serde(skip_serializing_if = "HashMap::is_empty")]
481 req headers: HashMap<String, String>,
482 opt pages: Cow<'a, str>,
483 opt zoom: f32,
484 opt page_width: f32,
485 opt page_height: f32,
486 opt margin_top: f32,
487 opt margin_bottom: f32,
488 opt margin_left: f32,
489 opt header_template: Cow<'a, str>,
490 opt footer_template: Cow<'a, str>,
491 opt wait_until: CaptureWaitUntil,
492 opt wait_for_element: Cow<'a, str>,
493 opt wait_time: u32,
494 opt css_media_type: CssMediaType,
495 opt filename: HashMap<String, String>,
496 opt engine: Cow<'a, str>,
497 opt engine_version: Cow<'a, str>,
498 opt timeout: u32,
499 }
500
501 pub struct ExportUrl<'a> {
505 operation: "export/url",
506
507 req input: Input<'a, 'a>,
509 req inline: bool,
510 req archive_multiple_files: bool,
511 }
512
513 pub struct ExportS3<'a> {
517 operation: "export/s3",
518
519 req input: Input<'a, 'a>,
521 req bucket: Cow<'a, str>,
522 req region: Cow<'a, str>,
523 req access_key_id: Cow<'a, str>,
524 req secret_access_key: Cow<'a, str>,
525 opt endpoint: Cow<'a, str>,
526 opt key: Cow<'a, str>,
527 opt key_prefix: Cow<'a, str>,
528 opt session_token: Cow<'a, str>,
529 opt acl: Cow<'a, str>,
530 opt cache_control: Cow<'a, str>,
531 opt content_disposition: Cow<'a, str>,
532 opt content_type: Cow<'a, str>,
533 opt metadata: serde_json::Value,
534 opt server_side_encrpytion: Cow<'a, str>,
535 opt tagging: serde_json::Value,
536 }
537
538 pub struct ExportAzureBlob<'a> {
542 operation: "export/azure/blob",
543
544 req input: Input<'a, 'a>,
546 req storage_account: Cow<'a, str>,
547 req container: Cow<'a, str>,
548 opt storage_access_key: Cow<'a, str>,
549 opt sas_token: Cow<'a, str>,
550 opt blob: Cow<'a, str>,
551 opt blob_prefix: Cow<'a, str>,
552 opt metadata: serde_json::Value,
553 }
554
555 pub struct ExportGoogleCloud<'a> {
559 operation: "export/google-cloud-storage",
560
561 req input: Input<'a, 'a>,
563 req project_id: Cow<'a, str>,
564 req bucket: Cow<'a, str>,
565 req client_email: Cow<'a, str>,
566 req private_key: Cow<'a, str>,
567 opt file: Cow<'a, str>,
568 opt file_prefix: Cow<'a, str>,
569 }
570
571 pub struct ExportOpenStack<'a> {
575 operation: "export/openstack",
576
577 req input: Input<'a, 'a>,
579 req auth_url: Cow<'a, str>,
580 req username: Cow<'a, str>,
581 req password: Cow<'a, str>,
582 req region: Cow<'a, str>,
583 req container: Cow<'a, str>,
584 opt tenant_name: Cow<'a, str>,
585 opt file: Cow<'a, str>,
586 opt file_prefix: Cow<'a, str>,
587 }
588
589 pub struct ExportSFTP<'a> {
593 operation: "export/sftp",
594
595 req input: Input<'a, 'a>,
597 req host: Cow<'a, str>,
598 req username: Cow<'a, str>,
599 opt port: u16,
600 opt password: Cow<'a, str>,
601 opt private_key: Cow<'a, str>,
602 opt file: Cow<'a, str>,
603 opt path: Cow<'a, str>,
604 }
605);
606
607#[derive(Debug, Serialize, PartialEq, Eq)]
611pub enum FontAlign {
612 #[serde(rename = "left")]
613 Left,
614 #[serde(rename = "right")]
615 Right,
616 #[serde(rename = "center")]
617 Center,
618}
619
620#[derive(Debug, Serialize, PartialEq, Eq)]
624pub enum WatermarkLayer {
625 #[serde(rename = "above")]
626 Above,
627 #[serde(rename = "below")]
628 Below,
629}
630
631#[derive(Debug, Serialize, PartialEq, Eq)]
635pub enum VerticalPosition {
636 #[serde(rename = "left")]
637 Left,
638 #[serde(rename = "right")]
639 Right,
640 #[serde(rename = "center")]
641 Center,
642}
643
644#[derive(Debug, Serialize, PartialEq, Eq)]
648pub enum HorizontalPosition {
649 #[serde(rename = "top")]
650 Top,
651 #[serde(rename = "bottom")]
652 Bottom,
653 #[serde(rename = "center")]
654 Center,
655}
656
657#[derive(Debug, Serialize, PartialEq, Eq)]
661pub enum ThumbnailFit {
662 #[serde(rename = "max")]
663 Max,
664 #[serde(rename = "crop")]
665 Crop,
666 #[serde(rename = "scale")]
667 Scale,
668}
669
670#[derive(Debug, Serialize, PartialEq, Eq)]
674pub enum OptimizationProfile {
675 #[serde(rename = "web")]
676 Web,
677 #[serde(rename = "print")]
678 Print,
679 #[serde(rename = "archive")]
680 Archive,
681 #[serde(rename = "mrc")]
682 Mrc,
683 #[serde(rename = "max")]
684 Max,
685}
686
687#[derive(Debug, Serialize, PartialEq, Eq)]
691pub enum Colorspace {
692 #[serde(rename = "unchanged")]
693 Unchanged,
694 #[serde(rename = "rgb")]
695 RGB,
696 #[serde(rename = "cmyk")]
697 CMYK,
698 #[serde(rename = "greyscale")]
699 Greyscale,
700}
701
702#[derive(Debug, Serialize, PartialEq, Eq)]
706pub enum CaptureWaitUntil {
707 #[serde(rename = "Load")]
708 Load,
709 #[serde(rename = "domcontentloaded")]
710 DOMContentLoaded,
711 #[serde(rename = "networkidle0")]
712 NetworkIdle0,
713 #[serde(rename = "networkidle2")]
714 NetworkIdle2,
715}
716
717#[derive(Debug, Serialize, PartialEq, Eq)]
721pub enum CssMediaType {
722 #[serde(rename = "print")]
723 Print,
724 #[serde(rename = "screen")]
725 Screen,
726}
727
728#[doc(hidden)]
729#[derive(Deserialize)]
730pub struct TasksOutput {
731 pub data: Status,
732}
733
734impl From<TasksOutput> for Status {
735 fn from(output: TasksOutput) -> Status {
736 output.data
737 }
738}
739
740#[derive(Debug, Deserialize)]
744pub struct Status {
745 pub id: String,
747
748 #[serde(default)]
750 pub job_id: Option<String>,
751
752 #[serde(default)]
754 pub name: Option<String>,
755
756 pub operation: String,
758
759 pub status: super::Status,
761
762 #[serde(default)]
766 #[serde(rename = "message")]
767 pub status_message: Option<String>,
768
769 #[serde(default)]
771 #[serde(rename = "code")]
772 pub error_code: Option<String>,
773
774 #[serde(default)]
776 #[serde(rename = "code")]
777 pub credits: Option<u16>,
778
779 #[serde(default)]
784 pub retry_of_task_id: Option<String>,
785
786 #[serde(default)]
790 pub retries: Vec<String>,
791
792 #[serde(default)]
793 pub engine: Option<String>,
795
796 #[serde(default)]
797 pub engine_version: Option<String>,
799
800 #[serde(default)]
802 pub payload: serde_json::Value,
803
804 #[serde(default)]
806 pub result: Option<HashMap<String, serde_json::Value>>,
807
808 #[serde(default)]
809 pub links: Option<HashMap<String, String>>,
810}