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}