1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use derive_builder::Builder;
use std::collections::HashMap;

use serde::de;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde_json::value::Value;

#[derive(Default, Deserialize, Serialize, Debug, Clone)]
pub struct Library {
    r#type: String,
    pub id: usize,
    pub name: String,
    pub links: Links,
}

#[derive(Default, Deserialize, Serialize, Debug, Clone)]
pub struct Links {
    #[serde(alias = "self")]
    pub self_link: Option<Link>,
    pub alternate: Link,
    // Only for collections
    pub up: Option<Link>,
}

#[derive(Default, Deserialize, Serialize, Debug, Clone)]
pub struct Link {
    pub href: String,
    pub r#type: String,
}

///! A struct representing a Zotero collection
#[derive(Default, Deserialize, Serialize, Debug, Clone)]
pub struct Collection {
    pub key: String,
    pub version: usize,
    pub library: Library,
    pub links: Links,
    pub meta: CollectionMeta,
    pub data: CollectionData,
}

///! This struct can be used to create a new Zotero collection
#[derive(Default, Deserialize, Serialize, Debug, Builder, Clone)]
#[serde(rename_all(deserialize = "camelCase", serialize = "camelCase"))]
#[builder(setter(into), default)]
pub struct CollectionData {
    #[serde(skip_serializing)]
    pub key: String,
    #[serde(skip_serializing)]
    pub version: usize,
    pub name: String,
    #[serde(deserialize_with = "deserialize_collection_parent")]
    pub parent_collection: StringOrBool,
    pub relations: HashMap<String, String>,
}

#[derive(Default, Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all(deserialize = "camelCase", serialize = "camelCase"))]
pub struct CollectionMeta {
    pub num_collections: Option<usize>,
    pub num_items: Option<usize>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum StringOrBool {
    r#Bool(bool),
    String(String),
}

impl Default for StringOrBool {
    fn default() -> StringOrBool {
        StringOrBool::Bool(false)
    }
}

/// A custom deserializer that deserialize parent_collection value either in bool or in String.
fn deserialize_collection_parent<'de, D>(deserializer: D) -> Result<StringOrBool, D::Error>
where
    D: Deserializer<'de>,
{
    let s: Value = Deserialize::deserialize(deserializer)?;
    if s.is_boolean() {
        Ok(StringOrBool::Bool(
            serde_json::from_value::<bool>(s).map_err(de::Error::custom)?,
        ))
    } else if s.is_string() {
        Ok(StringOrBool::String(
            serde_json::from_value::<String>(s).map_err(de::Error::custom)?,
        ))
    } else {
        panic!("invalid value")
    }
}