use crate::blueprint::store::BlueprintId;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Mutex;
use thiserror::Error;
pub mod inmemory;
pub use inmemory::InMemoryIssueStore;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IssueId(pub String);
impl IssueId {
pub fn new<S: Into<String>>(s: S) -> Self {
Self(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for IssueId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IssuePayload {
pub issue_id: IssueId,
pub blueprint_id: BlueprintId,
pub intent: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum IssueStatus {
Pending,
InFlight,
Applied {
new_version: String,
},
Rejected {
reason: String,
},
}
#[derive(Debug, Error)]
pub enum IssueStoreError {
#[error("issue not found: {0}")]
NotFound(IssueId),
#[error("issue already exists: {0}")]
Duplicate(IssueId),
#[error("other: {0}")]
Other(String),
}
#[async_trait]
pub trait IssueStore: Send + Sync {
fn name(&self) -> &str;
async fn create(&self, payload: IssuePayload) -> Result<(), IssueStoreError>;
async fn get(&self, id: &IssueId) -> Result<IssuePayload, IssueStoreError>;
async fn status(&self, id: &IssueId) -> Result<IssueStatus, IssueStoreError>;
async fn list(&self) -> Result<Vec<(IssueId, IssueStatus)>, IssueStoreError>;
async fn pop_pending(&self) -> Result<Option<IssuePayload>, IssueStoreError>;
async fn update_status(&self, id: &IssueId, status: IssueStatus)
-> Result<(), IssueStoreError>;
}
#[derive(Default)]
pub(crate) struct Inner {
pub(crate) order: Vec<IssueId>,
pub(crate) payloads: HashMap<IssueId, IssuePayload>,
pub(crate) statuses: HashMap<IssueId, IssueStatus>,
pub(crate) pending: std::collections::VecDeque<IssueId>,
}
pub(crate) type SharedInner = Mutex<Inner>;