diff options
author | Daniel Baumann <daniel@debian.org> | 2024-11-04 11:30:10 +0100 |
---|---|---|
committer | Daniel Baumann <daniel@debian.org> | 2025-01-23 21:00:02 +0100 |
commit | 816c704d1586954492cd9cffab04652d4e5a6c03 (patch) | |
tree | 2f97789629e5de8597ee7cb59d386590ac6c6f05 /src/wiki.rs | |
parent | Initial commit. (diff) | |
download | forgejo-cli-816c704d1586954492cd9cffab04652d4e5a6c03.tar.xz forgejo-cli-816c704d1586954492cd9cffab04652d4e5a6c03.zip |
Adding upstream version 0.2.0.upstream
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to '')
-rw-r--r-- | src/wiki.rs | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/wiki.rs b/src/wiki.rs new file mode 100644 index 0000000..2048a35 --- /dev/null +++ b/src/wiki.rs @@ -0,0 +1,158 @@ +use std::path::PathBuf; + +use base64ct::Encoding; +use clap::{Args, Subcommand}; +use eyre::{Context, OptionExt}; +use forgejo_api::Forgejo; + +use crate::{ + repo::{RepoArg, RepoInfo, RepoName}, + SpecialRender, +}; + +#[derive(Args, Clone, Debug)] +pub struct WikiCommand { + /// The local git remote that points to the repo to operate on. + #[clap(long, short = 'R')] + remote: Option<String>, + #[clap(subcommand)] + command: WikiSubcommand, +} + +#[derive(Subcommand, Clone, Debug)] +pub enum WikiSubcommand { + Contents { + repo: Option<RepoArg>, + }, + View { + #[clap(long, short)] + repo: Option<RepoArg>, + page: String, + }, + Clone { + repo: Option<RepoArg>, + #[clap(long, short)] + path: Option<PathBuf>, + }, + Browse { + #[clap(long, short)] + repo: Option<RepoArg>, + page: String, + }, +} + +impl WikiCommand { + pub async fn run(self, keys: &mut crate::KeyInfo, host_name: Option<&str>) -> eyre::Result<()> { + use WikiSubcommand::*; + + let repo = RepoInfo::get_current(host_name, self.repo(), self.remote.as_deref(), &keys)?; + let api = keys.get_api(repo.host_url()).await?; + let repo = repo + .name() + .ok_or_else(|| eyre::eyre!("couldn't guess repo"))?; + + match self.command { + Contents { repo: _ } => wiki_contents(repo, &api).await?, + View { repo: _, page } => view_wiki_page(repo, &api, &page).await?, + Clone { repo: _, path } => clone_wiki(repo, &api, path).await?, + Browse { repo: _, page } => browse_wiki_page(repo, &api, &page).await?, + } + Ok(()) + } + + fn repo(&self) -> Option<&RepoArg> { + use WikiSubcommand::*; + match &self.command { + Contents { repo } | View { repo, .. } | Clone { repo, .. } | Browse { repo, .. } => { + repo.as_ref() + } + } + } +} + +async fn wiki_contents(repo: &RepoName, api: &Forgejo) -> eyre::Result<()> { + let SpecialRender { bullet, .. } = *crate::special_render(); + + let query = forgejo_api::structs::RepoGetWikiPagesQuery { + page: None, + limit: None, + }; + let pages = api + .repo_get_wiki_pages(repo.owner(), repo.name(), query) + .await?; + for page in pages { + let title = page + .title + .as_deref() + .ok_or_eyre("page does not have title")?; + println!("{bullet} {title}"); + } + + Ok(()) +} + +async fn view_wiki_page(repo: &RepoName, api: &Forgejo, page: &str) -> eyre::Result<()> { + let SpecialRender { bold, reset, .. } = *crate::special_render(); + + let page = api + .repo_get_wiki_page(repo.owner(), repo.name(), page) + .await?; + + let title = page + .title + .as_deref() + .ok_or_eyre("page does not have title")?; + println!("{bold}{title}{reset}"); + println!(); + + let contents_b64 = page + .content_base64 + .as_deref() + .ok_or_eyre("page does not have content")?; + let contents = String::from_utf8(base64ct::Base64::decode_vec(contents_b64)?) + .wrap_err("page content is not utf-8")?; + + println!("{}", crate::markdown(&contents)); + Ok(()) +} + +async fn browse_wiki_page(repo: &RepoName, api: &Forgejo, page: &str) -> eyre::Result<()> { + let page = api + .repo_get_wiki_page(repo.owner(), repo.name(), page) + .await?; + let html_url = page + .html_url + .as_ref() + .ok_or_eyre("page does not have html url")?; + open::that_detached(html_url.as_str()).wrap_err("Failed to open URL")?; + Ok(()) +} + +async fn clone_wiki(repo: &RepoName, api: &Forgejo, path: Option<PathBuf>) -> eyre::Result<()> { + let repo_data = api.repo_get(repo.owner(), repo.name()).await?; + let clone_url = repo_data + .clone_url + .as_ref() + .ok_or_eyre("repo does not have clone url")?; + let git_stripped = clone_url + .as_str() + .strip_suffix(".git") + .unwrap_or(clone_url.as_str()); + let clone_url = url::Url::parse(&format!("{}.wiki.git", git_stripped))?; + + let repo_name = repo_data + .name + .as_deref() + .ok_or_eyre("repo does not have name")?; + let repo_full_name = repo_data + .full_name + .as_deref() + .ok_or_eyre("repo does not have full name")?; + let name = format!("{}'s wiki", repo_full_name); + + let path = path.unwrap_or_else(|| PathBuf::from(format!("./{repo_name}-wiki"))); + + crate::repo::clone_repo(&name, &clone_url, &path)?; + + Ok(()) +} |