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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::document::TypedCouchDocument;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;

#[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
#[serde(bound(deserialize = "T: TypedCouchDocument"))]
pub struct ViewCollection<K: DeserializeOwned, V: DeserializeOwned, T: TypedCouchDocument> {
    pub offset: Option<u32>,
    pub rows: Vec<ViewItem<K, V, T>>,
    pub total_rows: Option<u32>,
}

pub type RawViewCollection<K, V> = ViewCollection<K, V, Value>;

#[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
#[serde(bound(deserialize = "T: TypedCouchDocument"))]
pub struct ViewItem<K: DeserializeOwned, V: DeserializeOwned, T: TypedCouchDocument> {
    pub key: K,
    pub value: V,
    pub id: Option<String>,
    // docs field, populated if query was ran with 'include_docs'
    pub doc: Option<T>,
}

/// `CouchViews` can be used to create one of more views in a particular design document.
#[derive(Serialize)]
pub struct CouchViews {
    views: HashMap<String, CouchFunc>,
    language: String,
}

/// Constructs a `CouchDB` View Function. See
/// [defining-a-view](https://docs.couchdb.org/en/stable/ddocs/views/nosql.html#defining-a-view) for
/// details.
///
/// ```
/// use couch_rs::types::view::CouchFunc;
/// let couch_func = CouchFunc {
///     map: "function (doc) { if (doc.CLIP == true) { emit(doc.CLIP); } }".to_string(),
///     reduce: None,
/// };
/// ```
#[derive(Serialize)]
pub struct CouchFunc {
    pub map: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reduce: Option<String>,
}

#[derive(serde::Serialize)]
pub struct CouchUpdate {
    updates: HashMap<String, String>,
}

impl CouchViews {
    #[must_use]
    pub fn new(view_name: &str, func: CouchFunc) -> Self {
        let mut couch_views = CouchViews::default();
        couch_views.add(view_name, func);
        couch_views
    }

    pub fn add(&mut self, name: &str, func: CouchFunc) {
        self.views.insert(name.to_string(), func);
    }
}

impl Default for CouchViews {
    fn default() -> Self {
        CouchViews {
            views: HashMap::new(),
            language: "javascript".to_string(),
        }
    }
}

impl CouchFunc {
    #[must_use]
    pub fn new(map: &str, reduce: Option<&str>) -> Self {
        CouchFunc {
            map: map.to_string(),
            reduce: reduce.map(std::string::ToString::to_string),
        }
    }
}

impl From<CouchViews> for serde_json::Value {
    fn from(v: CouchViews) -> Self {
        serde_json::to_value(v).unwrap()
    }
}

impl From<CouchFunc> for serde_json::Value {
    fn from(f: CouchFunc) -> Self {
        serde_json::to_value(f).unwrap()
    }
}

impl CouchUpdate {
    #[must_use]
    pub fn new(func_name: &str, func: &str) -> Self {
        let mut update = CouchUpdate {
            updates: HashMap::new(),
        };
        update.add(func_name, func);
        update
    }

    pub fn add(&mut self, name: &str, func: &str) {
        self.updates.insert(name.to_string(), func.to_string());
    }
}

impl From<CouchUpdate> for serde_json::Value {
    fn from(u: CouchUpdate) -> Self {
        serde_json::to_value(u).unwrap()
    }
}