trello/
list.rs

1use crate::card::Card;
2use crate::client::Client;
3use crate::formatting::header;
4use crate::trello_error::TrelloError;
5use crate::trello_object::{Renderable, TrelloObject};
6
7use colored::*;
8use regex::RegexBuilder;
9use serde::Deserialize;
10
11type Result<T> = std::result::Result<T, TrelloError>;
12
13// https://developers.trello.com/reference/#list-object
14#[derive(Deserialize, Debug, Eq, PartialEq, Clone)]
15#[serde(rename_all = "camelCase")]
16pub struct List {
17    pub id: String,
18    pub name: String,
19    pub closed: bool,
20    pub cards: Option<Vec<Card>>,
21}
22
23impl TrelloObject for List {
24    fn get_type() -> String {
25        String::from("List")
26    }
27
28    fn get_name(&self) -> &str {
29        &self.name
30    }
31
32    fn get_fields() -> &'static [&'static str] {
33        &["id", "name", "closed"]
34    }
35}
36
37impl Renderable for List {
38    fn render(&self) -> String {
39        let title = header(&self.name, "-").bold().to_string();
40        let mut result: Vec<String> = vec![title];
41        if let Some(cards) = &self.cards {
42            for c in cards {
43                trace!("{:?}", c);
44                let mut lformat: Vec<String> = vec![];
45
46                if c.desc != "" {
47                    lformat.push("[...]".dimmed().to_string());
48                }
49
50                if let Some(labels) = &c.labels {
51                    for l in labels {
52                        lformat.push(l.render());
53                    }
54                }
55
56                let s = format!("* {} {}", &c.name, lformat.join(" "));
57
58                // trim end in case there is no data presented by lformat
59                result.push(s.trim_end().to_string());
60            }
61        }
62        result.join("\n")
63    }
64}
65
66impl List {
67    pub fn new(id: &str, name: &str, cards: Option<Vec<Card>>) -> List {
68        List {
69            id: String::from(id),
70            name: String::from(name),
71            cards,
72            closed: false,
73        }
74    }
75
76    /// Filters cards that match the given label_filter (As a regular expression).
77    /// Returns a copy of the original List, with the correct filtering applied.
78    ///
79    /// ```
80    /// use trello::{Card, Label, List};
81    ///
82    /// let list = List::new(
83    ///     "123",
84    ///     "TODO",
85    ///     Some(vec![
86    ///         Card::new("1", "Orange", "", Some(vec![Label::new("", "fruit", "")]), ""),
87    ///         Card::new("2", "Green", "", None, ""),
88    ///     ])
89    /// );
90    ///
91    /// assert_eq!(
92    ///     list.filter("idontexist"),
93    ///     List::new(
94    ///         "123",
95    ///         "TODO",
96    ///         Some(vec![]),
97    ///     )
98    /// );
99    ///
100    /// assert_eq!(
101    ///     list.filter("fruit"),
102    ///     List::new(
103    ///         "123",
104    ///         "TODO",
105    ///         Some(vec![
106    ///             Card::new("1", "Orange", "", Some(vec![Label::new("", "fruit", "")]), "")
107    ///         ])
108    ///     )
109    /// );
110    /// ```
111    pub fn filter(&self, label_filter: &str) -> List {
112        let re = RegexBuilder::new(label_filter)
113            .case_insensitive(true)
114            .build()
115            .expect("Invalid regex for label filter");
116
117        let closure = |c: &Card| -> bool {
118            if let Some(labels) = &c.labels {
119                for label in labels {
120                    if re.is_match(&label.name) {
121                        return true;
122                    }
123                }
124            }
125            false
126        };
127
128        let mut result = self.clone();
129        result.cards = if let Some(cards) = result.cards {
130            Some(cards.into_iter().filter(closure).collect())
131        } else {
132            None
133        };
134        result
135    }
136
137    pub fn create(client: &Client, board_id: &str, name: &str) -> Result<List> {
138        let url = client.get_trello_url("/1/lists/", &[])?;
139
140        let params = [("name", name), ("idBoard", board_id)];
141
142        Ok(reqwest::Client::new()
143            .post(url)
144            .form(&params)
145            .send()?
146            .error_for_status()?
147            .json()?)
148    }
149
150    pub fn open(client: &Client, list_id: &str) -> Result<List> {
151        let url = client.get_trello_url(&format!("/1/lists/{}", &list_id), &[])?;
152
153        let params = [("closed", "false")];
154
155        Ok(reqwest::Client::new()
156            .put(url)
157            .form(&params)
158            .send()?
159            .error_for_status()?
160            .json()?)
161    }
162
163    pub fn update(client: &Client, list: &List) -> Result<List> {
164        let url = client.get_trello_url(&format!("/1/lists/{}/", &list.id), &[])?;
165
166        let params = [("name", &list.name), ("closed", &list.closed.to_string())];
167
168        Ok(reqwest::Client::new()
169            .put(url)
170            .form(&params)
171            .send()?
172            .error_for_status()?
173            .json()?)
174    }
175
176    pub fn get_all(client: &Client, board_id: &str, cards: bool) -> Result<Vec<List>> {
177        let fields = List::get_fields().join(",");
178        let mut params = vec![("fields", fields.as_str())];
179
180        if cards {
181            params.push(("cards", "open"));
182        }
183
184        let url = client.get_trello_url(&format!("/1/boards/{}/lists", board_id), &params)?;
185
186        Ok(reqwest::get(url)?.error_for_status()?.json()?)
187    }
188}