use std::path::PathBuf; use axum::extract::{Path, Query, State}; use export::rest::AppResponseData; use jwt::Session; use serde::Deserialize; use crate::errors::{ApiError, GitApiError}; use crate::handler::resolve_reader; use crate::service::GitService; use crate::share::errors::GitError; #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, utoipa::ToSchema)] #[serde(rename_all = "snake_case")] pub enum TreeView { Entries, LastUpdated, } #[derive(Debug, Clone, Deserialize, utoipa::IntoParams)] pub struct GetTreeQuery { #[serde(default)] pub path: String, #[serde(default)] pub recurse: bool, #[serde(default)] pub resolve: bool, #[serde(default)] pub show_trees: bool, #[serde(default)] pub view: Option, } #[utoipa::path( get, path = "/repos/{space_name}/{repo_name}/tree/{revision}", tag = "trees", description = "读取修订下的目录树,并支持递归、符号链接解析和更新时间视图。", params( crate::handler::RepoPathRev, GetTreeQuery, ), responses( (status = 200, description = "tree payload: entries list, single entry (resolve), or last-updated list (view=last_updated)", body = AppResponseData) ) )] pub async fn get_tree( State(service): State, session: Session, Path(path): Path, Query(q): Query, ) -> Result, ApiError> { let _ = resolve_reader( &service, &session, path.space_name.clone(), path.repo_name.clone(), ) .await?; let out = match q.view { Some(TreeView::LastUpdated) => { let p = (!q.path.is_empty()).then(|| q.path.clone()); let rows = service .tree_last_updated(path.space_name, path.repo_name, path.revision, p) .await .map_err(GitApiError::from)?; serde_json::to_value(rows).map_err(json_err)? } _ => { if q.resolve && !q.path.is_empty() { let (entry, target, resolved) = service .tree_follow(path.space_name, path.repo_name, path.revision, q.path) .await .map_err(GitApiError::from)?; serde_json::json!({ "entry": entry, "symlink_target": target, "resolved": resolved, }) } else if q.recurse { let entries = service .tree_walk( path.space_name, path.repo_name, path.revision, true, q.show_trees, ) .await .map_err(GitApiError::from)?; serde_json::to_value(entries).map_err(json_err)? } else { let entries = service .tree_read( path.space_name, path.repo_name, path.revision, PathBuf::from(&q.path), ) .await .map_err(GitApiError::from)?; serde_json::to_value(entries).map_err(json_err)? } } }; Ok(AppResponseData::ok(out)) } fn json_err(e: serde_json::Error) -> ApiError { GitApiError::from(GitError::Internal(e.to_string())).into() }