use std::path::PathBuf;
use async_trait::async_trait;
use crate::tickets::api::backends::Backend;
use crate::tickets::api::backends::github::GitHubBackend;
use crate::tickets::api::config::GithubConfig;
use super::resolve::{IntentTokenResolver, SpecLookup, TicketData, TicketFetcher};
use super::types::IsrError;
pub struct BackendTicketFetcher {
token_resolver: Box<dyn IntentTokenResolver>,
}
impl BackendTicketFetcher {
#[must_use]
pub fn new(token_resolver: Box<dyn IntentTokenResolver>) -> Self {
Self { token_resolver }
}
}
#[async_trait]
impl TicketFetcher for BackendTicketFetcher {
async fn fetch(
&self,
owner: &str,
repo: &str,
ticket_id: &str,
) -> Result<TicketData, IsrError> {
let token = self.token_resolver.token(owner, repo).await?;
let cfg = GithubConfig {
token: Some(token),
owner: Some(owner.to_string()),
repo: Some(repo.to_string()),
gh_cli_user: None,
gh_cli_host: None,
};
let backend = GitHubBackend::new(cfg).map_err(|e| IsrError::TicketFetch(e.to_string()))?;
let id = ticket_id.trim_start_matches('#');
let issue = backend
.get_issue(id)
.await
.map_err(|e| IsrError::TicketFetch(e.to_string()))?;
Ok(TicketData {
id: format!("#{}", issue.id),
title: issue.title,
body: issue.description.unwrap_or_default(),
url: issue.url,
backend: issue.backend,
})
}
}
pub struct FsSpecLookup {
repo_root: PathBuf,
}
impl FsSpecLookup {
#[must_use]
pub fn new(repo_root: impl Into<PathBuf>) -> Self {
Self {
repo_root: repo_root.into(),
}
}
}
impl SpecLookup for FsSpecLookup {
fn load(&self, spec_file: &str) -> Option<String> {
let root = self.repo_root.canonicalize().ok()?;
let candidate = root.join(spec_file).canonicalize().ok()?;
if !candidate.starts_with(&root) {
return None;
}
std::fs::read_to_string(candidate).ok()
}
}