1use crate::client::TrelloClient;
2use crate::formatting::header;
3use crate::label::Label;
4use crate::trello_error::TrelloError;
5use crate::trello_object::{Renderable, TrelloObject};
6
7use chrono::{DateTime, Utc};
8use colored::Colorize;
9use serde::Deserialize;
10use std::str::FromStr;
11
12type Result<T> = std::result::Result<T, TrelloError>;
13
14#[derive(Deserialize, Debug, Eq, PartialEq, Clone)]
16#[serde(rename_all = "camelCase")]
17pub struct Card {
18 pub id: String,
19 pub name: String,
20 pub desc: String,
21 pub closed: bool,
22 pub url: String,
23 pub labels: Option<Vec<Label>>,
24 pub due: Option<DateTime<Utc>>,
25}
26
27impl TrelloObject for Card {
28 fn get_type() -> String {
29 String::from("Card")
30 }
31
32 fn get_name(&self) -> &str {
33 &self.name
34 }
35
36 fn get_fields() -> &'static [&'static str] {
37 &["id", "name", "desc", "labels", "closed", "due", "url"]
38 }
39}
40
41impl Renderable for Card {
42 fn render(&self, headers: bool) -> String {
43 match headers {
44 true => [header(&self.name, "=").as_str(), &self.desc].join("\n"),
45 false => self.desc.clone(),
46 }
47 }
48
49 fn simple_render(&self) -> String {
50 let mut lformat: Vec<String> = vec![];
51
52 if self.closed {
53 lformat.push("[Closed]".red().to_string());
54 }
55
56 lformat.push(String::from(&self.name));
57
58 if self.desc != "" {
59 lformat.push("[...]".dimmed().to_string());
60 }
61
62 if let Some(labels) = &self.labels {
63 for l in labels {
64 lformat.push(l.simple_render());
65 }
66 }
67
68 lformat.join(" ").trim_end().to_string()
70 }
71}
72
73#[derive(Debug, PartialEq, Eq)]
74pub struct CardContents {
75 pub name: String,
76 pub desc: String,
77}
78
79impl FromStr for CardContents {
80 type Err = TrelloError;
81
82 fn from_str(value: &str) -> Result<CardContents> {
103 let mut contents = value.split('\n').collect::<Vec<&str>>();
105 trace!("{:?}", contents);
106
107 let mut name = vec![contents.remove(0)];
109
110 let mut found = false;
114 while !contents.is_empty() {
115 let line = contents.remove(0);
116
117 if line.chars().take_while(|c| c == &'=').collect::<String>() != line {
118 name.push(line);
119 } else {
120 found = true;
121 break;
122 }
123 }
124
125 if !found {
126 return Err(TrelloError::CardParse(
127 "Unable to find name delimiter '===='".to_owned(),
128 ));
129 }
130
131 let name = name.join("\n");
132 let desc = contents.join("\n");
134
135 Ok(CardContents { name, desc })
136 }
137}
138
139impl Card {
140 pub fn new(
141 id: &str,
142 name: &str,
143 desc: &str,
144 labels: Option<Vec<Label>>,
145 url: &str,
146 due: Option<DateTime<Utc>>,
147 ) -> Card {
148 Card {
149 id: String::from(id),
150 name: String::from(name),
151 desc: String::from(desc),
152 url: String::from(url),
153 labels,
154 due,
155 closed: false,
156 }
157 }
158
159 pub fn get(client: &TrelloClient, card_id: &str) -> Result<Card> {
160 let url = client
161 .config
162 .get_trello_url(&format!("/1/cards/{}", card_id), &[])?;
163
164 Ok(client.client.get(url).send()?.error_for_status()?.json()?)
165 }
166
167 pub fn create(client: &TrelloClient, list_id: &str, card: &Card) -> Result<Card> {
168 let url = client.config.get_trello_url("/1/cards/", &[])?;
169
170 let params: [(&str, &str); 3] = [
171 ("name", &card.name),
172 ("desc", &card.desc),
173 ("idList", list_id),
174 ];
175
176 Ok(client
177 .client
178 .post(url)
179 .form(¶ms)
180 .send()?
181 .error_for_status()?
182 .json()?)
183 }
184
185 pub fn open(client: &TrelloClient, card_id: &str) -> Result<Card> {
186 let url = client
187 .config
188 .get_trello_url(&format!("/1/cards/{}", &card_id), &[])?;
189
190 let params = [("closed", "false")];
191
192 Ok(client
193 .client
194 .put(url)
195 .form(¶ms)
196 .send()?
197 .error_for_status()?
198 .json()?)
199 }
200
201 pub fn update(client: &TrelloClient, card: &Card) -> Result<Card> {
202 let url = client
203 .config
204 .get_trello_url(&format!("/1/cards/{}/", &card.id), &[])?;
205
206 let params = [
207 ("name", &card.name),
208 ("desc", &card.desc),
209 ("closed", &card.closed.to_string()),
210 ];
211
212 Ok(client
213 .client
214 .put(url)
215 .form(¶ms)
216 .send()?
217 .error_for_status()?
218 .json()?)
219 }
220
221 pub fn change_list(client: &TrelloClient, card_id: &str, list_id: &str) -> Result<()> {
223 let url = client
224 .config
225 .get_trello_url(&format!("/1/cards/{}/", card_id), &[])?;
226
227 let params = [("idList", list_id)];
228
229 client
230 .client
231 .put(url)
232 .form(¶ms)
233 .send()?
234 .error_for_status()?;
235
236 Ok(())
237 }
238
239 pub fn get_all(client: &TrelloClient, list_id: &str) -> Result<Vec<Card>> {
240 let url = client.config.get_trello_url(
241 &format!("/1/lists/{}/cards/", list_id),
242 &[("fields", &Card::get_fields().join(","))],
243 )?;
244 Ok(client.client.get(url).send()?.error_for_status()?.json()?)
245 }
246}