1use async_trait::async_trait;
2use facet::Facet;
3use facet_json::{DeserializeError, JsonError};
4use issuecraft_ql::{
5 CloseReason, ExecutionEngine, ExecutionResult, IqlQuery, IssueId, IssueKind, ProjectId, UserId,
6};
7
8#[derive(thiserror::Error, Debug)]
9pub enum ClientError {
10 #[error("Not implemented")]
11 NotImplemented,
12 #[error("This action is not supported by the chosen backend")]
13 NotSupported,
14 #[error("IQL error: {0}")]
15 IqlError(#[from] issuecraft_ql::IqlError),
16 #[error("Deserialization error: {0}")]
17 DeserializationError(#[from] DeserializeError<JsonError>),
18 #[error("Client specific: {0}")]
19 ClientSpecific(String),
20}
21
22#[derive(Debug, Clone, Facet)]
23pub struct UserInfo {
24 pub name: String,
25 #[facet( skip_serializing_if = Option::is_none)]
26 pub display: Option<String>,
27 pub email: String,
28}
29
30#[derive(Debug, Clone, Facet)]
31pub struct ProjectInfo {
32 #[facet(skip_serializing_if = Option::is_none)]
33 pub description: Option<String>,
34 pub owner: UserId,
35 #[facet(skip_serializing_if = Option::is_none)]
36 pub display: Option<String>,
37}
38
39#[derive(Debug, Clone, Facet)]
40#[repr(C)]
41pub enum IssueStatus {
42 Open,
43 Assigned,
44 Blocked,
45 Closed { reason: CloseReason },
46}
47
48#[derive(Debug, Clone, Facet)]
49#[repr(C)]
50pub enum Priority {
51 Low,
52 Medium,
53 High,
54 Critical,
55}
56
57#[derive(Debug, Clone, Facet)]
58pub struct IssueInfo {
59 pub title: String,
60 pub kind: IssueKind,
61 #[facet(skip_serializing_if = Option::is_none)]
62 pub description: Option<String>,
63 pub status: IssueStatus,
64 pub project: ProjectId,
65 #[facet(skip_serializing_if = Option::is_none)]
66 pub priority: Option<Priority>,
67 #[facet(skip_serializing_if = Option::is_none)]
68 pub assignee: Option<UserId>,
69}
70
71impl IssueInfo {
72 pub fn is_closed(&self) -> bool {
73 matches!(self.status, IssueStatus::Closed { .. })
74 }
75}
76
77#[derive(Debug, Clone, Facet)]
78pub struct CommentInfo {
79 pub issue: IssueId,
80 pub created_at: time::UtcDateTime,
81 pub content: String,
82 pub author: UserId,
83}
84
85#[derive(Debug, Clone)]
86pub enum AuthenticationInfo {
87 Password { password: String },
88 Token { token: String },
89 Certificate { path: Vec<u8> },
90}
91
92#[derive(Debug, Clone)]
93pub struct LoginInfo {
94 pub user: String,
95 pub auth: AuthenticationInfo,
96}
97
98#[async_trait]
99pub trait Client {
100 async fn login(&mut self, _login: LoginInfo) -> Result<(), ClientError> {
101 Err(ClientError::NotSupported)
102 }
103 async fn logout(&mut self) -> Result<(), ClientError> {
104 Err(ClientError::NotSupported)
105 }
106 async fn query(&mut self, query: &IqlQuery) -> Result<ExecutionResult, ClientError>;
107}
108
109#[async_trait]
110impl<E: ExecutionEngine + Send> Client for E {
111 async fn query(&mut self, query: &IqlQuery) -> Result<ExecutionResult, ClientError> {
112 let result = self.execute(query).await?;
113 Ok(result)
114 }
115}
116
117pub trait Backend {
118 fn init(&mut self) {}
119}