use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
pub type Url = String;
pub type JsonValue = Box<::serde_json::Value>;
pub type TimestampString = String;
macro_rules! define_string_wrapper {
($($(#[$meta:meta])* $vis:vis $name:ident;)*) => { $(
$(#[$meta])*
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
$vis struct $name(String);
impl $name {
pub fn new(id: String) -> Self {
$name(id)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl AsRef<str> for $name {
fn as_ref(&self) -> &str {
self.as_str()
}
}
)* };
}
define_string_wrapper! {
pub DriveId;
pub ItemId;
pub Tag;
}
#[doc(hidden)]
pub trait ResourceField {
fn api_field_name(&self) -> &'static str;
}
macro_rules! define_resource_object {
($(
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident #$field_enum_name:ident {
$(
$(#[$field_meta:meta])*
$([unselectable]
pub $unsel_field_name:ident
$(@$field_rename:literal)?
)?
$(pub $sel_field_name:ident)?
: Option<$field_ty:ty>,
)*
}
)*) => {
$(
$(#[$meta])*
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
$vis struct $struct_name {
$(
#[allow(missing_docs)]
$(#[$field_meta])*
$($(#[serde(rename = $field_rename)])? pub $unsel_field_name)?
$(pub $sel_field_name)?
: Option<$field_ty>,
)*
#[serde(default)]
_private: (),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
$vis enum $field_enum_name {
$(
$( #[allow(missing_docs, non_camel_case_types)]
$sel_field_name,
)?
)*
}
impl ResourceField for $field_enum_name {
#[inline]
fn api_field_name(&self) -> &'static str {
lazy_static! {
static ref FIELD_NAME_TABLE: Vec<String> = {
vec![
$($(snake_to_camel_case(stringify!($sel_field_name)),)?)*
]
};
}
&FIELD_NAME_TABLE[*self as usize]
}
}
)*
};
}
define_resource_object! {
#[derive(Debug)]
pub struct Drive #DriveField {
pub id: Option<DriveId>,
pub created_by: Option<JsonValue>,
pub created_date_time: Option<TimestampString>,
pub description: Option<String>,
pub drive_type: Option<JsonValue>,
pub items: Option<Vec<DriveItem>>,
pub last_modified_by: Option<JsonValue>,
pub last_modified_date_time: Option<TimestampString>,
pub name: Option<String>,
pub owner: Option<JsonValue>,
pub quota: Option<JsonValue>,
pub root: Option<DriveItem>,
pub sharepoint_ids: Option<JsonValue>,
pub special: Option<Vec<DriveItem>>,
pub system: Option<JsonValue>,
pub web_url: Option<Url>,
}
#[derive(Debug)]
pub struct DriveItem #DriveItemField {
pub audio: Option<JsonValue>,
pub content: Option<JsonValue>,
pub c_tag: Option<Tag>,
pub deleted: Option<JsonValue>,
pub description: Option<String>,
pub file: Option<JsonValue>,
pub file_system_info: Option<JsonValue>,
pub folder: Option<JsonValue>,
pub image: Option<JsonValue>,
pub location: Option<JsonValue>,
pub package: Option<JsonValue>,
pub photo: Option<JsonValue>,
pub publication: Option<JsonValue>,
pub remote_item: Option<JsonValue>,
pub root: Option<JsonValue>,
pub search_result: Option<JsonValue>,
pub shared: Option<JsonValue>,
pub sharepoint_ids: Option<JsonValue>,
pub size: Option<i64>,
pub special_folder: Option<JsonValue>,
pub video: Option<JsonValue>,
pub web_dav_url: Option<Url>,
pub children: Option<Vec<DriveItem>>,
pub created_by_user: Option<JsonValue>,
pub last_modified_by_user: Option<JsonValue>,
pub permissions: Option<JsonValue>,
pub thumbnails: Option<JsonValue>,
pub versions: Option<JsonValue>,
pub id: Option<ItemId>,
pub created_by: Option<JsonValue>,
pub created_date_time: Option<TimestampString>,
pub e_tag: Option<Tag>,
pub last_modified_by: Option<JsonValue>,
pub last_modified_date_time: Option<TimestampString>,
pub name: Option<String>,
pub parent_reference: Option<JsonValue>,
pub web_url: Option<Url>,
[unselectable]
pub download_url @"@microsoft.graph.downloadUrl": Option<Url>,
}
}
#[inline]
fn snake_to_camel_case(s: &str) -> String {
let mut buf = String::new();
let mut is_u = false;
for c in s.chars() {
if c == '_' {
is_u = true;
} else if is_u {
is_u = false;
buf.push(c.to_ascii_uppercase());
} else {
buf.push(c);
}
}
buf
}
#[allow(missing_docs)]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorObject {
pub code: Option<String>,
pub message: Option<String>,
pub inner_error: Option<Box<ErrorObject>>,
#[serde(flatten)]
pub extra_data: serde_json::Map<String, serde_json::Value>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_snake_to_camel_case() {
let data = [
("abc", "abc"),
("hello_world", "helloWorld"),
("wh_tst_ef_ck", "whTstEfCk"),
];
for (i, o) in &data {
assert_eq!(snake_to_camel_case(i), *o);
}
}
#[test]
fn test_api_field_name() {
assert_eq!(DriveField::id.api_field_name(), "id");
assert_eq!(DriveField::drive_type.api_field_name(), "driveType");
assert_eq!(DriveField::owner.api_field_name(), "owner");
assert_eq!(DriveField::web_url.api_field_name(), "webUrl");
assert_eq!(DriveItemField::id.api_field_name(), "id");
assert_eq!(
DriveItemField::file_system_info.api_field_name(),
"fileSystemInfo"
);
assert_eq!(DriveItemField::size.api_field_name(), "size");
assert_eq!(DriveItemField::web_dav_url.api_field_name(), "webDavUrl");
assert_eq!(DriveItemField::web_url.api_field_name(), "webUrl");
}
}