use crate::cache::{Crate, CrateCache, CrateTar, FileContent};
use crate::download::CrateDownloader;
use crate::github::{GithubClient, Issue, IssueEvent, Repository};
use crate::{
CrateVersion, CrateVersionPath, Directory, FileLineRange, Item, ItemQuery, Line, LineQuery,
};
#[derive(Clone)]
pub struct RustAssistant {
downloader: CrateDownloader,
cache: CrateCache,
github: GithubClient,
}
impl From<(CrateDownloader, CrateCache, GithubClient)> for RustAssistant {
fn from((downloader, cache, github): (CrateDownloader, CrateCache, GithubClient)) -> Self {
Self {
downloader,
cache,
github,
}
}
}
impl RustAssistant {
pub async fn get_crate(&self, crate_version: &CrateVersion) -> anyhow::Result<Crate> {
Ok(match self.cache.get_crate(crate_version) {
None => {
let data = self.downloader.download_crate_file(crate_version).await?;
let crate_tar = CrateTar::from((crate_version.clone(), data));
let krate =
tokio::task::spawn_blocking(move || Crate::try_from(crate_tar)).await??;
self.cache.set_crate(crate_version.clone(), krate);
self.cache
.get_crate(crate_version)
.ok_or_else(|| anyhow::anyhow!("Failed to get crate: {}", crate_version))?
}
Some(crate_tar) => crate_tar,
})
}
pub async fn get_file_content(
&self,
crate_version_path: &CrateVersionPath,
file_line_range: FileLineRange,
) -> anyhow::Result<Option<FileContent>> {
let krate = self.get_crate(&crate_version_path.crate_version).await?;
let path = crate_version_path.path.clone();
tokio::task::spawn_blocking(move || {
krate.get_file_by_file_line_range(path.as_ref(), file_line_range)
})
.await?
}
pub async fn read_directory(
&self,
crate_version_path: CrateVersionPath,
) -> anyhow::Result<Option<Directory>> {
let krate = self.get_crate(&crate_version_path.crate_version).await?;
Ok(krate
.read_directory(crate_version_path.path.as_ref())
.cloned())
}
pub async fn search_item(
&self,
crate_version: &CrateVersion,
query: impl Into<ItemQuery>,
) -> anyhow::Result<Vec<Item>> {
let krate = self.get_crate(crate_version).await?;
let query = query.into();
Ok(tokio::task::spawn_blocking(move || krate.search_item(&query)).await?)
}
pub async fn search_line(
&self,
crate_version: &CrateVersion,
query: impl Into<LineQuery>,
) -> anyhow::Result<Vec<Line>> {
let krate = self.get_crate(crate_version).await?;
let query = query.into();
tokio::task::spawn_blocking(move || krate.search_line(&query)).await?
}
pub async fn read_github_repository_file(
&self,
repo: &Repository,
path: &str,
branch: impl Into<Option<&str>>,
) -> anyhow::Result<Option<FileContent>> {
self.github.get_file(repo, path, branch).await
}
pub async fn read_github_repository_directory(
&self,
repo: &Repository,
path: &str,
branch: impl Into<Option<&str>>,
) -> anyhow::Result<Option<Directory>> {
self.github.read_dir(repo, path, branch).await
}
pub async fn search_github_repository_for_issues(
&self,
repo: &Repository,
query: &str,
) -> anyhow::Result<Vec<Issue>> {
self.github.search_for_issues(repo, query).await
}
pub async fn get_github_repository_issue_timeline(
&self,
repo: &Repository,
issue_number: u64,
) -> anyhow::Result<Vec<IssueEvent>> {
self.github.get_issue_timeline(repo, issue_number).await
}
pub async fn get_github_repository_branches(
&self,
repo: &Repository,
) -> anyhow::Result<Vec<String>> {
self.github.get_repo_branches(repo).await
}
}