Skip to main content

asana_sdk/
models.rs

1use serde::{Deserialize};
2use serde::de::DeserializeOwned;
3
4/// A macro for generating Asana model structs
5///
6/// # Examples
7///
8/// The example below will generate a `User` struct, linked to the `/users` Asana endpoint.
9///
10/// * It will include & deserialize the `email` & `name` fields as Strings.
11/// * The fields `gid` and `resource_type` are included by default, and don't have to be specified.
12///
13/// Any extra fields returned from the Asana API are flattened by [`Serde`] into a [`Hashmap`]:
14///
15/// ```
16/// use asana_sdk::models::Model;
17///
18/// model!(User "users" {
19///     email: String,
20///     name: String,
21/// });
22/// ```
23///
24/// Comma-separated includes are defined after the struct. Their deserialization type must be indicated within the struct.
25/// This will usually be a `Vec<Type>` or `Option<Type>`.
26///
27/// This example is suitable for getting a set of Tasks, with the set of Projects that tasks belongs to, and the Assignee the task is assigned to.
28/// Of course, you can include more fields than just `name` on the includes.
29/// ```
30/// model!(Assignee "assignee" {
31///     name: String
32/// });
33///
34/// model!(Project "projects" {
35///     name: String
36/// });
37///
38/// model!(Tasks "tasks" {
39///     name: String,
40///     assignee: Option<Assignee>
41///     projects: Vec<Project>
42/// } Project, Assignee);
43/// ```
44#[macro_export]
45macro_rules! model {
46    ($name:ident $endpoint:literal { $( $field:ident: $fty:ty ),* $(,)? } $( $include:ident),* $(,)? ) => {
47        #[derive(serde::Serialize, serde::Deserialize, Debug)]
48        pub struct $name {
49            gid: String,
50            resource_type: String,
51            $( $field: $fty, )*
52            #[serde(flatten)]
53            extra: std::collections::HashMap<String, serde_json::Value>,
54        }
55
56        impl Model for $name {
57            fn endpoint() -> String { $endpoint.to_string() }
58
59            fn opt_strings() -> Vec<String> {
60                vec![$(format!("{}.({})", $include::endpoint(), $include::field_names().join("|"))),*]
61            }
62
63            fn field_names() -> &'static [&'static str] {
64                &["resource_type", $(stringify!($field)),*]
65            }
66        }
67    };
68}
69
70pub trait Model: DeserializeOwned {
71    fn endpoint() -> String;
72    fn field_names() -> &'static [&'static str];
73    fn opt_strings() -> Vec<String>;
74}
75
76#[derive(Deserialize, Debug)]
77pub(crate) struct Wrapper<T> {
78    pub data: T,
79}
80
81#[derive(Deserialize, Debug)]
82pub(crate) struct ListWrapper<T> {
83    pub data: Vec<T>,
84}