use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::ToSchema;
use crate::{AppService, error::AppError};
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ReadmeDto {
pub content: String,
pub html: String,
}
impl AppService {
pub async fn git_repo_readme(
&self,
ctx: &Session,
wk_name: &str,
repo_name: &str,
) -> Result<Option<ReadmeDto>, AppError> {
let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
let readme_names = [
"README.md",
"README.markdown",
"README.txt",
"README",
"Readme.md",
"readme.md",
];
for name in &readme_names {
match self
.git_tree_entry_by_path_from_commit_for_readme(&repo.id, name)
.await?
{
Some((content, oid)) => {
return self
.git_blob_load_for_readme(&repo, &content, &oid)
.await;
}
None => continue,
}
}
Ok(None)
}
}
impl AppService {
async fn git_tree_entry_by_path_from_commit_for_readme(
&self,
repo_id: &uuid::Uuid,
readme_name: &str,
) -> Result<Option<(String, String)>, AppError> {
use crate::git::rpc_err;
use git::rpc::proto as p;
use git::rpc::proto::tree_service_client::TreeServiceClient;
let mut client = TreeServiceClient::new(self.git.clone());
let mut commit_client =
git::rpc::proto::commit_service_client::CommitServiceClient::new(
self.git.clone(),
);
let summary_resp = commit_client
.commit_summary(tonic::Request::new(p::CommitSummaryRequest {
repo_id: repo_id.to_string(),
}))
.await
.map_err(rpc_err)?
.into_inner();
let head_commit = match summary_resp.summary.and_then(|s| s.head) {
Some(c) => c,
None => return Ok(None),
};
let tree_id = match head_commit.tree_id {
Some(id) => id.value,
None => return Ok(None),
};
let resp = client
.tree_entry_by_path(tonic::Request::new(
p::TreeEntryByPathRequest {
repo_id: repo_id.to_string(),
tree_oid: Some(p::ObjectId {
value: tree_id.clone(),
}),
path: readme_name.to_string(),
},
))
.await
.map_err(rpc_err)?
.into_inner();
match resp.entry {
Some(entry) => {
let oid = entry.oid.map(|o| o.value).unwrap_or_default();
if oid.is_empty() {
Ok(None)
} else {
Ok(Some((readme_name.to_string(), oid)))
}
}
None => Ok(None),
}
}
async fn git_blob_load_for_readme(
&self,
repo: &model::repos::RepoModel,
_path: &str,
oid: &str,
) -> Result<Option<super::readme::ReadmeDto>, AppError> {
use crate::git::rpc_err;
use git::rpc::proto as p;
use git::rpc::proto::blob_service_client::BlobServiceClient;
let mut client = BlobServiceClient::new(self.git.clone());
let resp = client
.blob_load(tonic::Request::new(p::BlobLoadRequest {
repo_id: repo.id.to_string(),
id: Some(p::ObjectId {
value: oid.to_string(),
}),
path: String::new(),
}))
.await
.map_err(rpc_err)?
.into_inner();
let content = String::from_utf8_lossy(&resp.blob).to_string();
if content.is_empty() {
return Ok(None);
}
let html = comrak::markdown_to_html(
&content,
&comrak::ComrakOptions::default(),
);
Ok(Some(super::readme::ReadmeDto { content, html }))
}
}