1#![allow(clippy::field_reassign_with_default)]
39use std::{env, fmt, fmt::Debug};
40
41use anyhow::{bail, Result};
42use chrono::{offset::Utc, DateTime};
43use reqwest::{header, Method, Request, StatusCode, Url};
44use schemars::JsonSchema;
45use serde::{
46 de::{DeserializeOwned, MapAccess, SeqAccess, Visitor},
47 Deserialize, Deserializer, Serialize,
48};
49
50const ENDPOINT: &str = "https://api.airtable.com/v0/";
52
53pub struct Airtable {
55 key: String,
56 base_id: String,
57 enterprise_account_id: String,
58
59 client: reqwest_middleware::ClientWithMiddleware,
60}
61
62pub fn api_key_from_env() -> String {
64 env::var("AIRTABLE_API_KEY").unwrap_or_default()
65}
66
67impl Airtable {
68 pub fn new<K, B, E>(key: K, base_id: B, enterprise_account_id: E) -> Self
74 where
75 K: ToString,
76 B: ToString,
77 E: ToString,
78 {
79 let http = reqwest::Client::builder().build();
80 match http {
81 Ok(c) => {
82 let retry_policy = reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
83 let client = reqwest_middleware::ClientBuilder::new(c)
84 .with(reqwest_tracing::TracingMiddleware)
86 .with(reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy))
88 .build();
89
90 Self {
91 key: key.to_string(),
92 base_id: base_id.to_string(),
93 enterprise_account_id: enterprise_account_id.to_string(),
94
95 client,
96 }
97 }
98 Err(e) => panic!("creating client failed: {:?}", e),
99 }
100 }
101
102 pub fn new_from_env() -> Self {
107 let base_id = env::var("AIRTABLE_BASE_ID").unwrap_or_default();
108 let enterprise_account_id = env::var("AIRTABLE_ENTERPRISE_ACCOUNT_ID").unwrap_or_default();
109
110 Airtable::new(api_key_from_env(), base_id, enterprise_account_id)
111 }
112
113 pub fn get_key(&self) -> &str {
115 &self.key
116 }
117
118 fn request<B>(&self, method: Method, path: String, body: B, query: Option<Vec<(&str, String)>>) -> Result<Request>
119 where
120 B: Serialize,
121 {
122 let base = Url::parse(ENDPOINT)?;
123 let url = base.join(&(self.base_id.to_string() + "/" + &path))?;
124
125 let bt = format!("Bearer {}", self.key);
126 let bearer = header::HeaderValue::from_str(&bt)?;
127
128 let mut headers = header::HeaderMap::new();
130 headers.append(header::AUTHORIZATION, bearer);
131 headers.append(
132 header::CONTENT_TYPE,
133 header::HeaderValue::from_static("application/json"),
134 );
135
136 let mut rb = self.client.request(method.clone(), url).headers(headers);
137
138 match query {
139 None => (),
140 Some(val) => {
141 rb = rb.query(&val);
142 }
143 }
144
145 if method != Method::GET && method != Method::DELETE {
147 rb = rb.json(&body);
148 }
149
150 Ok(rb.build()?)
152 }
153
154 pub async fn list_records<T: DeserializeOwned>(
156 &self,
157 table: &str,
158 view: &str,
159 fields: Vec<&str>,
160 ) -> Result<Vec<Record<T>>> {
161 let mut params = vec![("pageSize", "100".to_string()), ("view", view.to_string())];
162 for field in fields {
163 params.push(("fields[]", field.to_string()));
164 }
165
166 let mut request = self.request(Method::GET, table.to_string(), (), Some(params))?;
168
169 let mut resp = self.client.execute(request).await?;
170 match resp.status() {
171 StatusCode::OK => (),
172 s => {
173 bail!("status code: {}, body: {}", s, resp.text().await?);
174 }
175 };
176
177 let mut r: APICall<T> = resp.json().await?;
179
180 let mut records = r.records;
181
182 let mut offset = r.offset;
183
184 while !offset.is_empty() {
187 request = self.request(
188 Method::GET,
189 table.to_string(),
190 (),
191 Some(vec![
192 ("pageSize", "100".to_string()),
193 ("view", view.to_string()),
194 ("offset", offset),
195 ]),
196 )?;
197
198 resp = self.client.execute(request).await?;
199 match resp.status() {
200 StatusCode::OK => (),
201 s => {
202 bail!("status code: {}, body: {}", s, resp.text().await?);
203 }
204 };
205
206 r = resp.json().await?;
208
209 records.append(&mut r.records);
210
211 offset = r.offset;
212 }
213
214 Ok(records)
215 }
216
217 pub async fn get_record<T: DeserializeOwned>(&self, table: &str, record_id: &str) -> Result<Record<T>> {
219 let request = self.request(Method::GET, format!("{}/{}", table, record_id), (), None)?;
221
222 let resp = self.client.execute(request).await?;
223 match resp.status() {
224 StatusCode::OK => (),
225 s => {
226 bail!("status code: {}, body: {}", s, resp.text().await?);
227 }
228 };
229
230 let record: Record<T> = resp.json().await?;
232
233 Ok(record)
234 }
235
236 pub async fn delete_record(&self, table: &str, record_id: &str) -> Result<()> {
238 let request = self.request(
240 Method::DELETE,
241 table.to_string(),
242 (),
243 Some(vec![("records[]", record_id.to_string())]),
244 )?;
245
246 let resp = self.client.execute(request).await?;
247 match resp.status() {
248 StatusCode::OK => (),
249 s => {
250 bail!("status code: {}, body: {}", s, resp.text().await?);
251 }
252 };
253
254 Ok(())
255 }
256
257 pub async fn create_records<T: Serialize + DeserializeOwned>(
262 &self,
263 table: &str,
264 records: Vec<Record<T>>,
265 ) -> Result<Vec<Record<T>>> {
266 let request = self.request(
268 Method::POST,
269 table.to_string(),
270 APICall {
271 records,
272 offset: "".to_string(),
273 typecast: Some(true),
274 },
275 None,
276 )?;
277
278 let resp = self.client.execute(request).await?;
279 match resp.status() {
280 StatusCode::OK => (),
281 s => {
282 bail!("status code: {}, body: {}", s, resp.text().await?);
283 }
284 };
285
286 let r: APICall<T> = resp.json().await?;
288
289 Ok(r.records)
290 }
291
292 pub async fn update_records<T: Serialize + DeserializeOwned>(
297 &self,
298 table: &str,
299 records: Vec<Record<T>>,
300 ) -> Result<Vec<Record<T>>> {
301 let request = self.request(
303 Method::PATCH,
304 table.to_string(),
305 APICall {
306 records,
307 offset: "".to_string(),
308 typecast: Some(true),
309 },
310 None,
311 )?;
312
313 let resp = self.client.execute(request).await?;
314 match resp.status() {
315 StatusCode::OK => (),
316 s => {
317 bail!("status code: {}, body: {}", s, resp.text().await?);
318 }
319 };
320
321 match resp.json::<APICall<T>>().await {
323 Ok(v) => Ok(v.records),
324 Err(_) => {
325 Ok(vec![])
327 }
328 }
329 }
330
331 pub async fn list_users(&self) -> Result<Vec<User>> {
335 if self.enterprise_account_id.is_empty() {
336 bail!("An enterprise account id is required.");
338 }
339
340 let request = self.request(
342 Method::GET,
343 format!("v0/meta/enterpriseAccounts/{}/users", self.enterprise_account_id),
344 (),
345 Some(vec![("state", "provisioned".to_string())]),
346 )?;
347
348 let resp = self.client.execute(request).await?;
349 match resp.status() {
350 StatusCode::OK => (),
351 s => {
352 bail!("status code: {}, body: {}", s, resp.text().await?);
353 }
354 };
355
356 let result: UsersResponse = resp.json().await?;
358
359 Ok(result.users)
360 }
361
362 pub async fn get_enterprise_user(&self, email: &str) -> Result<EnterpriseUser> {
367 if self.enterprise_account_id.is_empty() {
368 bail!("An enterprise account id is required.");
370 }
371
372 let request = self.request(
374 Method::GET,
375 format!("v0/meta/enterpriseAccounts/{}/users", self.enterprise_account_id),
376 (),
377 Some(vec![
378 ("email", email.to_string()),
379 ("include", "collaborations".to_string()),
380 ]),
381 )?;
382
383 let resp = self.client.execute(request).await?;
384
385 match resp.status() {
386 StatusCode::OK => (),
387 s => {
388 bail!("status code: {}, body: {}", s, resp.text().await?);
389 }
390 };
391
392 let r: EnterpriseUsersResponse = resp.json().await?;
393
394 if r.users.is_empty() {
395 bail!("no user was returned");
396 }
397
398 Ok(r.users.get(0).unwrap().clone())
399 }
400
401 pub async fn add_collaborator_to_workspace(
406 &self,
407 workspace_id: &str,
408 user_id: &str,
409 permission_level: &str,
410 ) -> Result<()> {
411 if self.enterprise_account_id.is_empty() {
412 bail!("An enterprise account id is required.");
414 }
415
416 let request = self.request(
418 Method::POST,
419 format!("v0/meta/workspaces/{}/collaborators", workspace_id),
420 NewCollaborator {
421 collaborators: vec![Collaborator {
422 user: User {
423 id: user_id.to_string(),
424 email: Default::default(),
425 name: Default::default(),
426 },
427 permission_level: permission_level.to_string(),
428 }],
429 },
430 None,
431 )?;
432
433 let resp = self.client.execute(request).await?;
434 match resp.status() {
435 StatusCode::OK => (),
436 s => {
437 bail!("status code: {}, body: {}", s, resp.text().await?);
438 }
439 };
440
441 Ok(())
442 }
443
444 pub async fn get_enterprise_workspace<const N: usize>(
448 &self,
449 workspace_id: &str,
450 includes: Option<[WorkspaceIncludes; N]>,
451 ) -> Result<Workspace> {
452 if self.enterprise_account_id.is_empty() {
453 bail!("An enterprise account id is required.");
455 }
456
457 let request = self.request(
459 Method::GET,
460 format!("v0/meta/workspaces/{}?", workspace_id),
461 (),
462 includes.map(|includes| {
463 includes
464 .map(|include| {
465 (
466 "include",
467 match include {
468 WorkspaceIncludes::Collaborators => "collaborators".to_string(),
469 WorkspaceIncludes::InviteLinks => "inviteLinks".to_string(),
470 },
471 )
472 })
473 .to_vec()
474 }),
475 )?;
476
477 let resp = self.client.execute(request).await?;
478 match resp.status() {
479 StatusCode::OK => (),
480 s => {
481 bail!("status code: {}, body: {}", s, resp.text().await?);
482 }
483 };
484
485 let r: Workspace = resp.json().await?;
486
487 Ok(r)
488 }
489
490 pub async fn delete_internal_user_by_email(&self, email: &str) -> Result<()> {
495 if self.enterprise_account_id.is_empty() {
496 bail!("An enterprise account id is required.");
498 }
499
500 let request = self.request(
502 Method::DELETE,
503 format!("v0/meta/enterpriseAccounts/{}/users", self.enterprise_account_id),
504 (),
505 Some(vec![("email", email.to_string())]),
506 )?;
507
508 let resp = self.client.execute(request).await?;
509 match resp.status() {
510 StatusCode::OK => (),
511 s => {
512 bail!("status code: {}, body: {}", s, resp.text().await?);
513 }
514 };
515
516 let result: DeleteUserResponse = resp.json().await?;
518 if !result.errors.is_empty() {
519 bail!("body: {:?}", result);
520 }
521
522 Ok(())
523 }
524}
525
526#[derive(Debug, Clone, Serialize, Deserialize)]
527struct APICall<T> {
528 #[serde(default, skip_serializing_if = "String::is_empty")]
532 pub offset: String,
533 pub records: Vec<Record<T>>,
535 #[serde(skip_serializing_if = "Option::is_none")]
540 pub typecast: Option<bool>,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct Record<T> {
546 #[serde(default, skip_serializing_if = "String::is_empty")]
547 pub id: String,
548 pub fields: T,
549 #[serde(skip_serializing_if = "Option::is_none")]
550 pub created_time: Option<DateTime<Utc>>,
551}
552
553#[derive(Debug, Default, Clone, Serialize, JsonSchema, Deserialize)]
555pub struct User {
556 #[serde(default, skip_serializing_if = "String::is_empty")]
557 pub id: String,
558 #[serde(default, skip_serializing_if = "String::is_empty")]
559 pub email: String,
560 #[serde(default, skip_serializing_if = "String::is_empty")]
561 pub name: String,
562}
563
564enum UserField {
565 Id,
566 Email,
567 Name,
568}
569
570const USERFIELDS: &[&str] = &["id", "email", "name"];
571
572impl<'de> Deserialize<'de> for UserField {
573 fn deserialize<D>(deserializer: D) -> Result<UserField, D::Error>
574 where
575 D: Deserializer<'de>,
576 {
577 struct UserFieldVisitor;
578
579 impl<'de> Visitor<'de> for UserFieldVisitor {
580 type Value = UserField;
581
582 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
583 formatter.write_str("`id` `email` or `name`")
584 }
585
586 fn visit_str<E>(self, value: &str) -> Result<UserField, E>
587 where
588 E: serde::de::Error,
589 {
590 match value {
591 "id" => Ok(UserField::Id),
592 "email" => Ok(UserField::Email),
593 "name" => Ok(UserField::Name),
594 _ => Err(serde::de::Error::unknown_field(value, USERFIELDS)),
595 }
596 }
597 }
598
599 deserializer.deserialize_identifier(UserFieldVisitor)
600 }
601}
602
603struct UserVisitor;
604
605impl<'de> Visitor<'de> for UserVisitor {
606 type Value = User;
607
608 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
609 formatter.write_str("struct User")
610 }
611
612 fn visit_seq<V>(self, mut seq: V) -> Result<User, V::Error>
613 where
614 V: SeqAccess<'de>,
615 {
616 let id = seq
617 .next_element()?
618 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
619 let email = seq
620 .next_element()?
621 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
622 let name = seq
623 .next_element()?
624 .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
625 Ok(User { id, email, name })
626 }
627
628 fn visit_map<V>(self, mut map: V) -> Result<User, V::Error>
629 where
630 V: MapAccess<'de>,
631 {
632 let mut id = None;
633 let mut email = None;
634 let mut name = None;
635 while let Some(key) = map.next_key()? {
636 match key {
637 UserField::Id => {
638 if id.is_some() {
639 return Err(serde::de::Error::duplicate_field("id"));
640 }
641 id = Some(map.next_value()?);
642 }
643 UserField::Email => {
644 if email.is_some() {
645 return Err(serde::de::Error::duplicate_field("email"));
646 }
647 email = Some(map.next_value()?);
648 }
649 UserField::Name => {
650 if name.is_some() {
651 return Err(serde::de::Error::duplicate_field("name"));
652 }
653 name = Some(map.next_value()?);
654 }
655 }
656 }
657 let id = id.unwrap_or_default();
658 let email = email.ok_or_else(|| serde::de::Error::missing_field("email"))?;
659 let name = name.unwrap_or_default();
660 Ok(User { id, email, name })
661 }
662}
663
664struct UsersVisitor;
665
666impl<'de> Visitor<'de> for UsersVisitor {
667 type Value = Vec<User>;
668
669 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
670 formatter.write_str("a very special vector")
671 }
672
673 fn visit_seq<A: SeqAccess<'de>>(self, mut access: A) -> Result<Self::Value, A::Error> {
674 let mut users: Vec<User> = Default::default();
675
676 while let Some(user) = access.next_element::<User>()? {
679 users.push(user);
680 }
681
682 Ok(users)
683 }
684}
685
686#[derive(Debug, Default, Clone, Serialize, Deserialize)]
688pub struct UsersResponse {
689 #[serde(default, skip_serializing_if = "Vec::is_empty")]
690 pub users: Vec<User>,
691}
692
693#[derive(Debug, Default, Clone, Serialize, Deserialize)]
696pub struct DeleteUserResponse {
697 #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "deletedUsers")]
698 pub deleted_users: Vec<User>,
699 #[serde(default, skip_serializing_if = "Vec::is_empty")]
700 pub errors: Vec<ErrorResponse>,
701}
702
703#[derive(Debug, Default, Clone, Serialize, Deserialize)]
704pub struct ErrorResponse {
705 #[serde(default, skip_serializing_if = "String::is_empty")]
706 pub email: String,
707 #[serde(default, skip_serializing_if = "String::is_empty", rename = "type")]
708 pub type_: String,
709 #[serde(default, skip_serializing_if = "String::is_empty")]
710 pub message: String,
711}
712
713#[derive(Debug, Default, Clone, Serialize, Deserialize)]
714pub struct AttachmentShort {
715 #[serde(default, skip_serializing_if = "String::is_empty")]
716 pub url: String,
717}
718
719#[derive(Debug, Default, Clone, Serialize, Deserialize)]
720pub struct Attachment {
721 #[serde(default, skip_serializing_if = "String::is_empty")]
722 pub id: String,
723 #[serde(default, skip_serializing_if = "String::is_empty")]
724 pub url: String,
725 #[serde(default, skip_serializing_if = "String::is_empty")]
726 pub filename: String,
727 #[serde(default)]
728 pub size: i64,
729 #[serde(default, skip_serializing_if = "String::is_empty", rename = "type")]
730 pub type_: String,
731 #[serde(default)]
732 pub thumbnails: Thumbnails,
733}
734
735#[derive(Debug, Default, Clone, Serialize, Deserialize)]
736pub struct Thumbnails {
737 #[serde(default)]
738 pub small: Full,
739 #[serde(default)]
740 pub large: Full,
741 #[serde(default)]
742 pub full: Full,
743}
744
745#[derive(Debug, Default, Clone, Serialize, Deserialize)]
746pub struct Full {
747 #[serde(default, skip_serializing_if = "String::is_empty")]
748 pub url: String,
749 #[serde(default)]
750 pub width: i64,
751 #[serde(default)]
752 pub height: i64,
753}
754
755#[derive(Debug, Default, Clone, Serialize, Deserialize)]
756pub struct NewCollaborator {
757 #[serde(default, skip_serializing_if = "Vec::is_empty")]
758 pub collaborators: Vec<Collaborator>,
759}
760
761#[derive(Debug, Default, Clone, Serialize, Deserialize)]
762pub struct Collaborator {
763 #[serde(default)]
764 pub user: User,
765 #[serde(
766 default,
767 skip_serializing_if = "String::is_empty",
768 deserialize_with = "deserialize_null_string::deserialize",
769 rename = "permissionLevel"
770 )]
771 pub permission_level: String,
772}
773
774#[derive(Debug, Default, Clone, Serialize, Deserialize)]
775pub struct EnterpriseUsersResponse {
776 #[serde(default, skip_serializing_if = "Vec::is_empty")]
777 pub users: Vec<EnterpriseUser>,
778}
779
780#[derive(Debug, Clone, Serialize, Deserialize)]
781pub struct EnterpriseUser {
782 #[serde(
783 default,
784 skip_serializing_if = "String::is_empty",
785 deserialize_with = "deserialize_null_string::deserialize"
786 )]
787 pub id: String,
788 #[serde(
789 default,
790 skip_serializing_if = "String::is_empty",
791 deserialize_with = "deserialize_null_string::deserialize"
792 )]
793 pub state: String,
794 #[serde(
795 default,
796 skip_serializing_if = "String::is_empty",
797 deserialize_with = "deserialize_null_string::deserialize"
798 )]
799 pub email: String,
800 #[serde(
801 default,
802 skip_serializing_if = "String::is_empty",
803 deserialize_with = "deserialize_null_string::deserialize"
804 )]
805 pub name: String,
806 #[serde(
807 default,
808 skip_serializing_if = "Option::is_none",
809 rename = "lastActivityTime",
810 deserialize_with = "deserialize_missing_timezone::deserialize"
811 )]
812 pub last_activity_time: Option<DateTime<Utc>>,
813 #[serde(
814 default,
815 skip_serializing_if = "String::is_empty",
816 deserialize_with = "deserialize_null_string::deserialize",
817 rename = "invitedToAirtableByUserId"
818 )]
819 pub invited_to_airtable_by_user_id: String,
820 #[serde(rename = "createdTime")]
821 pub created_time: DateTime<Utc>,
822 #[serde(default)]
823 pub collaborations: Collaborations,
824}
825
826#[derive(Debug, Default, Clone, Serialize, Deserialize)]
827pub struct Collaborations {
828 #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "workspaceCollaborations")]
829 pub workspace_collaborations: Vec<Collaboration>,
830 #[serde(default, skip_serializing_if = "Vec::is_empty", rename = "baseCollaborations")]
831 pub base_collaborations: Vec<Collaboration>,
832}
833
834#[derive(Debug, Clone, Serialize, Deserialize)]
835pub struct Collaboration {
836 #[serde(
837 default,
838 skip_serializing_if = "String::is_empty",
839 deserialize_with = "deserialize_null_string::deserialize",
840 rename = "baseId"
841 )]
842 pub base_id: String,
843 #[serde(
844 default,
845 skip_serializing_if = "String::is_empty",
846 deserialize_with = "deserialize_null_string::deserialize",
847 rename = "permissionLevel"
848 )]
849 pub permission_level: String,
850 #[serde(rename = "createdTime")]
851 pub created_time: DateTime<Utc>,
852 #[serde(
853 default,
854 skip_serializing_if = "String::is_empty",
855 deserialize_with = "deserialize_null_string::deserialize",
856 rename = "grantedByUserId"
857 )]
858 pub granted_by_user_id: String,
859 #[serde(
860 default,
861 skip_serializing_if = "String::is_empty",
862 deserialize_with = "deserialize_null_string::deserialize",
863 rename = "workspaceId"
864 )]
865 pub workspace_id: String,
866}
867
868pub enum WorkspaceIncludes {
871 Collaborators,
872 InviteLinks,
873}
874
875#[derive(Debug, Clone, Serialize, Deserialize)]
876pub struct Workspace {
877 pub id: String,
878 pub name: String,
879 pub created_time: Option<DateTime<Utc>>,
880 #[serde(rename = "baseIds")]
881 pub base_ids: Vec<String>,
882 #[serde(rename = "individualCollaborators")]
883 pub individual_collaborators: Option<WorkspaceCollaborators>,
884 #[serde(rename = "baseCollaborators")]
885 pub group_collaborators: Option<WorkspaceCollaborators>,
886 pub invite_links: Option<InviteLinks>,
887}
888
889#[derive(Debug, Clone, Serialize, Deserialize)]
890pub struct WorkspaceCollaborators {
891 #[serde(rename = "workspaceCollaborators")]
892 pub workspace_collaborators: Vec<WorkspaceCollaborator>,
893 #[serde(rename = "baseCollaborators")]
894 pub base_collaborators: Vec<BaseCollaborator>,
895}
896
897#[derive(Debug, Clone, Serialize, Deserialize)]
898pub struct WorkspaceCollaborator {
899 #[serde(rename = "userId")]
900 pub user_id: String,
901 pub email: String,
902 #[serde(rename = "permissionLevel")]
903 pub permission_level: String,
904 #[serde(rename = "createdTime")]
905 pub created_time: Option<DateTime<Utc>>,
906 #[serde(rename = "grantedByUserId")]
907 pub granted_by_user_id: String,
908}
909
910#[derive(Debug, Clone, Serialize, Deserialize)]
911pub struct BaseCollaborator {
912 #[serde(rename = "baseId")]
913 pub base_id: String,
914 #[serde(rename = "userId")]
915 pub user_id: String,
916 pub email: String,
917 #[serde(rename = "permissionLevel")]
918 pub permission_level: String,
919 #[serde(rename = "createdTime")]
920 pub created_time: Option<DateTime<Utc>>,
921 #[serde(rename = "grantedByUserId")]
922 pub granted_by_user_id: String,
923}
924
925#[derive(Debug, Clone, Serialize, Deserialize)]
926pub struct InviteLinks {
927 pub workspace_invite_links: Vec<WorkspaceInviteLink>,
928 pub base_invite_links: Vec<BaseInviteLink>,
929}
930
931#[derive(Debug, Clone, Serialize, Deserialize)]
932pub struct WorkspaceInviteLink {
933 pub id: String,
934 #[serde(rename = "type")]
935 pub _type: String,
936 #[serde(rename = "invitedEmail")]
937 pub invited_email: String,
938 #[serde(rename = "restrictedToEmailDomains")]
939 pub restricted_to_email_domains: Vec<String>,
940 #[serde(rename = "createdTime")]
941 pub created_time: Option<DateTime<Utc>>,
942 #[serde(rename = "permissionLevel")]
943 pub permission_level: String,
944 #[serde(rename = "referredByUserId")]
945 pub referred_by_user_id: String,
946}
947
948#[derive(Debug, Clone, Serialize, Deserialize)]
949pub struct BaseInviteLink {
950 pub id: String,
951 #[serde(rename = "baseId")]
952 pub base_id: String,
953 #[serde(rename = "type")]
954 pub _type: String,
955 #[serde(rename = "invitedEmail")]
956 pub invited_email: String,
957 #[serde(rename = "restrictedToEmailDomains")]
958 pub restricted_to_email_domains: Vec<String>,
959 #[serde(rename = "createdTime")]
960 pub created_time: Option<DateTime<Utc>>,
961 #[serde(rename = "permissionLevel")]
962 pub permission_level: String,
963 #[serde(rename = "referredByUserId")]
964 pub referred_by_user_id: String,
965}
966
967struct AttachmentsVisitor;
968
969impl<'de> Visitor<'de> for AttachmentsVisitor {
970 type Value = Vec<Attachment>;
971
972 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
973 formatter.write_str("a very special vector")
974 }
975
976 fn visit_seq<A: SeqAccess<'de>>(self, mut access: A) -> Result<Self::Value, A::Error> {
977 let mut attachments: Vec<Attachment> = Default::default();
978
979 while let Some(attachment) = access.next_element::<Attachment>()? {
982 attachments.push(attachment);
983 }
984
985 Ok(attachments)
986 }
987}
988
989pub mod user_format_as_array_of_strings {
990 use serde::{self, ser::SerializeSeq, Deserializer, Serializer};
991
992 use super::{User, UsersVisitor};
993
994 pub fn serialize<S>(array: &[String], serializer: S) -> Result<S::Ok, S::Error>
1002 where
1003 S: Serializer,
1004 {
1005 let mut seq = serializer.serialize_seq(Some(array.len())).unwrap();
1007 for e in array {
1008 seq.serialize_element(&User {
1009 id: Default::default(),
1010 email: e.to_string(),
1011 name: Default::default(),
1012 })
1013 .unwrap();
1014 }
1015 seq.end()
1016 }
1017
1018 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
1026 where
1027 D: Deserializer<'de>,
1028 {
1029 let airtable_users = deserializer.deserialize_seq(UsersVisitor {}).unwrap();
1030
1031 let mut users: Vec<String> = Default::default();
1032 for a in airtable_users {
1033 users.push(a.email.to_string());
1034 }
1035
1036 Ok(users)
1037 }
1038}
1039
1040pub mod user_format_as_string {
1041 use serde::{self, ser::SerializeStruct, Deserializer, Serializer};
1042
1043 use super::{UserVisitor, USERFIELDS};
1044
1045 pub fn serialize<S>(email: &str, serializer: S) -> Result<S::Ok, S::Error>
1053 where
1054 S: Serializer,
1055 {
1056 let mut state = serializer.serialize_struct("User", 1)?;
1057 state.serialize_field("email", &email)?;
1058 state.end()
1059 }
1060
1061 pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
1069 where
1070 D: Deserializer<'de>,
1071 {
1072 let user = deserializer
1073 .deserialize_struct("User", USERFIELDS, UserVisitor)
1074 .unwrap();
1075 Ok(user.email)
1076 }
1077}
1078
1079pub mod attachment_format_as_array_of_strings {
1080 use serde::{self, ser::SerializeSeq, Deserializer, Serializer};
1081
1082 use super::{AttachmentShort, AttachmentsVisitor};
1083
1084 pub fn serialize<S>(array: &[String], serializer: S) -> Result<S::Ok, S::Error>
1092 where
1093 S: Serializer,
1094 {
1095 let mut seq = serializer.serialize_seq(Some(array.len())).unwrap();
1097 for e in array {
1098 let mut attachment: AttachmentShort = Default::default();
1099 attachment.url = e.to_string();
1100 seq.serialize_element(&attachment).unwrap();
1101 }
1102 seq.end()
1103 }
1104
1105 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
1113 where
1114 D: Deserializer<'de>,
1115 {
1116 let airtable_attachments = deserializer.deserialize_seq(AttachmentsVisitor {}).unwrap();
1117
1118 let mut attachments: Vec<String> = Default::default();
1119 for a in airtable_attachments {
1120 attachments.push(a.url.to_string());
1121 }
1122
1123 Ok(attachments)
1124 }
1125}
1126
1127pub mod attachment_format_as_string {
1128 use serde::{self, ser::SerializeSeq, Deserializer, Serializer};
1129
1130 use super::{Attachment, AttachmentsVisitor};
1131
1132 pub fn serialize<S>(url: &str, serializer: S) -> Result<S::Ok, S::Error>
1140 where
1141 S: Serializer,
1142 {
1143 let mut seq = serializer.serialize_seq(Some(1)).unwrap();
1145 let mut attachment: Attachment = Default::default();
1146 attachment.url = url.to_string();
1147 seq.serialize_element(&attachment).unwrap();
1148 seq.end()
1149 }
1150
1151 pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
1159 where
1160 D: Deserializer<'de>,
1161 {
1162 let airtable_attachments = deserializer.deserialize_seq(AttachmentsVisitor {}).unwrap();
1163 let mut url = String::new();
1164 if !airtable_attachments.is_empty() {
1165 url = airtable_attachments[0].url.to_string();
1166 }
1167 Ok(url)
1168 }
1169}
1170
1171#[derive(Debug, Default, Clone, Serialize, JsonSchema, Deserialize)]
1173pub struct Barcode {
1174 #[serde(default, skip_serializing_if = "String::is_empty")]
1175 pub text: String,
1176 #[serde(default, skip_serializing_if = "String::is_empty", rename = "type")]
1177 pub type_: String,
1178}
1179
1180struct BarcodeVisitor;
1181
1182impl<'de> Visitor<'de> for BarcodeVisitor {
1183 type Value = Barcode;
1184
1185 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1186 formatter.write_str("struct Barcode")
1187 }
1188
1189 fn visit_seq<V>(self, mut seq: V) -> Result<Barcode, V::Error>
1190 where
1191 V: SeqAccess<'de>,
1192 {
1193 let text = seq
1194 .next_element()?
1195 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
1196 let type_ = seq
1197 .next_element()?
1198 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
1199 Ok(Barcode { text, type_ })
1200 }
1201
1202 fn visit_map<V>(self, mut map: V) -> Result<Barcode, V::Error>
1203 where
1204 V: MapAccess<'de>,
1205 {
1206 let mut text = None;
1207 let mut type_ = None;
1208 while let Some(key) = map.next_key()? {
1209 match key {
1210 BarcodeField::Text => {
1211 if text.is_some() {
1212 return Err(serde::de::Error::duplicate_field("text"));
1213 }
1214 text = Some(map.next_value()?);
1215 }
1216 BarcodeField::Type => {
1217 if type_.is_some() {
1218 return Err(serde::de::Error::duplicate_field("type"));
1219 }
1220 type_ = Some(map.next_value()?);
1221 }
1222 }
1223 }
1224 let text = text.ok_or_else(|| serde::de::Error::missing_field("text"))?;
1225 let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
1226 Ok(Barcode { text, type_ })
1227 }
1228}
1229
1230enum BarcodeField {
1231 Text,
1232 Type,
1233}
1234
1235const BARCODEFIELDS: &[&str] = &["text", "type"];
1236
1237impl<'de> Deserialize<'de> for BarcodeField {
1238 fn deserialize<D>(deserializer: D) -> Result<BarcodeField, D::Error>
1239 where
1240 D: Deserializer<'de>,
1241 {
1242 struct BarcodeFieldVisitor;
1243
1244 impl<'de> Visitor<'de> for BarcodeFieldVisitor {
1245 type Value = BarcodeField;
1246
1247 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1248 formatter.write_str("`text` or `type`")
1249 }
1250
1251 fn visit_str<E>(self, value: &str) -> Result<BarcodeField, E>
1252 where
1253 E: serde::de::Error,
1254 {
1255 match value {
1256 "text" => Ok(BarcodeField::Text),
1257 "type" => Ok(BarcodeField::Type),
1258 _ => Err(serde::de::Error::unknown_field(value, BARCODEFIELDS)),
1259 }
1260 }
1261 }
1262
1263 deserializer.deserialize_identifier(BarcodeFieldVisitor)
1264 }
1265}
1266
1267pub mod barcode_format_as_string {
1268 use serde::{self, ser::SerializeStruct, Deserializer, Serializer};
1269
1270 use super::{BarcodeVisitor, BARCODEFIELDS};
1271
1272 pub fn serialize<S>(text: &str, serializer: S) -> Result<S::Ok, S::Error>
1280 where
1281 S: Serializer,
1282 {
1283 let mut state = serializer.serialize_struct("Barcode", 1)?;
1284 state.serialize_field("text", &text)?;
1285 state.serialize_field("type", "code39")?;
1287 state.end()
1288 }
1289
1290 pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
1298 where
1299 D: Deserializer<'de>,
1300 {
1301 let barcode = deserializer
1302 .deserialize_struct("Barcode", BARCODEFIELDS, BarcodeVisitor)
1303 .unwrap();
1304 Ok(barcode.text)
1305 }
1306}
1307
1308pub mod deserialize_null_string {
1309 use serde::{self, Deserialize, Deserializer};
1310
1311 pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
1319 where
1320 D: Deserializer<'de>,
1321 {
1322 let s = String::deserialize(deserializer).unwrap_or_default();
1323
1324 Ok(s)
1325 }
1326}
1327
1328pub mod deserialize_missing_timezone {
1331 use chrono::{DateTime, Utc};
1332 use serde::{self, de::Error, Deserialize, Deserializer};
1333
1334 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
1342 where
1343 D: Deserializer<'de>,
1344 {
1345 let mut s = String::deserialize(deserializer).unwrap_or_default();
1348
1349 DateTime::parse_from_rfc3339(s.as_str())
1350 .or_else(|_| {
1351 s += "Z";
1355 DateTime::parse_from_rfc3339(s.as_str())
1356 })
1357 .map(|fixed_offset| Some(fixed_offset.with_timezone(&Utc)))
1358 .map_err(D::Error::custom)
1359 }
1360}