issuecraft_core/
lib.rs

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