1use async_trait::async_trait;
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4
5use crate::{Repository, Result};
6
7platform_number!(IssueNumber);
8
9#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
10#[serde(rename_all = "snake_case")]
11pub enum OpenClosed {
12 Open,
13 Closed,
14}
15
16#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
17pub struct Label {
18 pub name: String,
19 pub color: String,
20 pub description: Option<String>,
21}
22
23#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
24pub struct Issue {
25 pub number: IssueNumber,
26 pub title: String,
27 pub body: Option<String>,
28 pub state: OpenClosed,
29 pub labels: Vec<Label>,
30 pub updated_at: DateTime<Utc>,
31}
32
33#[async_trait]
34pub trait IssuesProvider: Send + Sync {
35 async fn issue(&self, repository: &Repository, number: IssueNumber) -> Result<Issue>;
36 async fn labels(&self, repository: &Repository) -> Result<Vec<Label>>;
37 async fn upsert_label(&self, repository: &Repository, label: &Label) -> Result<Label>;
38}
39
40#[cfg(test)]
41mod tests {
42 use super::IssueNumber;
43 use crate::ModelError;
44
45 #[test]
46 fn platform_numbers_are_one_based() {
47 assert_eq!(IssueNumber::new(0), Err(ModelError::InvalidNumber));
48 assert_eq!(IssueNumber::new(42).expect("positive").get(), 42);
49 }
50}