1use crate::Project;
2use crate::{Client, WeakClient};
3use futures::prelude::*;
4use phabricator_api::edge::search::Search as EdgeSearch;
5use phabricator_api::edge::search::Type as EdgeType;
6use phabricator_api::maniphest::search::Projects;
7use phabricator_api::maniphest::search::SearchData;
8use phabricator_api::types::Phid;
9use phabricator_api::RequestError;
10use rust_decimal::prelude::*;
11use std::collections::hash_map::Entry;
12use std::collections::HashMap;
13use std::sync::Arc;
14use std::sync::Mutex;
15
16#[derive(Clone, Debug)]
17pub struct Task {
18 id: u32,
19 phid: Phid,
20 client: WeakClient,
21 inner: Arc<Mutex<Inner>>,
22}
23
24#[derive(Clone, Debug, Default)]
25struct Inner {
26 title: String,
27 description: String,
28 status: String,
29 projects: Option<Vec<Project>>,
30 parents: Option<Vec<Task>>,
31 subtasks: Option<Vec<Task>>,
32 points: Option<Decimal>,
33}
34
35impl Task {
36 fn map_projects(projects: Projects, cache: &HashMap<Phid, Project>) -> Vec<Project> {
37 projects
38 .projects
39 .iter()
40 .map(|phid| cache[phid].clone())
41 .collect()
42 }
43
44 fn from_searchdata(data: SearchData, client: &Client, cache: &HashMap<Phid, Project>) -> Task {
45 let projects = data
46 .attachments
47 .projects
48 .map(|p| Self::map_projects(p, cache));
49
50 let inner = Arc::new(Mutex::new(Inner {
51 title: data.fields.name,
52 description: data.fields.description,
53 points: data.fields.points,
54 status: data.fields.status.value,
55 projects,
56 subtasks: None,
57 parents: None,
58 }));
59
60 Task {
61 id: data.id,
62 client: client.downgrade(),
63 phid: data.phid,
64 inner,
65 }
66 }
67
68 fn update_searchdata(&mut self, data: SearchData, cache: &HashMap<Phid, Project>) {
69 let projects = data
70 .attachments
71 .projects
72 .map(|p| Self::map_projects(p, cache));
73 let mut inner = self.inner.lock().unwrap();
74 inner.title = data.fields.name;
75 inner.description = data.fields.description;
76 inner.points = data.fields.points;
77 inner.status = data.fields.status.value;
78 inner.projects = projects;
79 }
81
82 pub(crate) fn update_from_searchdata(data: SearchData, client: &Client) -> Task {
83 client.update_cache(|cache| match cache.tasks.entry(data.id) {
84 Entry::Vacant(v) => v
85 .insert(Self::from_searchdata(data, client, &cache.projects))
86 .clone(),
87 Entry::Occupied(mut o) => {
88 let t = o.get_mut();
89 t.update_searchdata(data, &cache.projects);
90 t.clone()
91 }
92 })
93 }
94
95 pub fn id(&self) -> u32 {
96 self.id
97 }
98
99 pub fn phid(&self) -> &Phid {
100 &self.phid
101 }
102
103 pub fn title(&self) -> String {
104 let l = self.inner.lock().unwrap();
105 l.title.clone()
106 }
107
108 pub fn description(&self) -> String {
109 let l = self.inner.lock().unwrap();
110 l.description.clone()
111 }
112
113 pub fn status(&self) -> String {
114 let l = self.inner.lock().unwrap();
115 l.status.clone()
116 }
117
118 pub fn points(&self) -> Option<Decimal> {
119 let l = self.inner.lock().unwrap();
120 l.points
121 }
122
123 pub(crate) fn projects_resolved(&self) -> bool {
124 let l = self.inner.lock().unwrap();
125 l.projects.is_some()
126 }
127
128 pub async fn projects(&self) -> Result<Vec<Project>, RequestError> {
129 {
130 let l = self.inner.lock().unwrap();
131 if let Some(ref projects) = l.projects {
132 return Ok(projects.clone());
133 }
134 }
135 let client = self.client.upgrade().unwrap();
136 client
137 .tasks(&[self.id])
138 .projects()
139 .query()
140 .try_for_each(|_| future::ready(Ok(())))
141 .await?;
142 let l = self.inner.lock().unwrap();
143 Ok(l.projects.as_ref().unwrap().clone())
144 }
145
146 async fn edges(&self, edge: EdgeType) -> Result<Vec<Task>, RequestError> {
147 let client = self.client.upgrade().unwrap();
149 let s = EdgeSearch {
151 sources: vec![self.phid.clone()],
152 types: vec![edge],
153 ..Default::default()
154 };
155
156 let r = client.client().request(&s).await?;
158
159 let mut phids = r.data.iter().map(|data| &data.dest);
160
161 let tasks = client
162 .tasks_by_phid(&mut phids)
163 .query()
164 .try_collect()
165 .await?;
166 Ok(tasks)
167 }
168
169 pub async fn parents(&self) -> Result<Vec<Task>, RequestError> {
170 {
171 let l = self.inner.lock().unwrap();
172 if let Some(ref parents) = l.parents {
173 return Ok(parents.clone());
174 }
175 }
176 let tasks = self.edges(EdgeType::TaskParent).await?;
177 let mut l = self.inner.lock().unwrap();
178 l.parents = Some(tasks);
179 Ok(l.parents.as_ref().unwrap().clone())
180 }
181
182 pub async fn subtasks(&self) -> Result<Vec<Task>, RequestError> {
183 {
184 let l = self.inner.lock().unwrap();
185 if let Some(ref subtasks) = l.subtasks {
186 return Ok(subtasks.clone());
187 }
188 }
189 let tasks = self.edges(EdgeType::TaskSubtask).await?;
190 let mut l = self.inner.lock().unwrap();
191 l.subtasks = Some(tasks);
192 Ok(l.subtasks.as_ref().unwrap().clone())
193 }
194}