summaryrefslogtreecommitdiffstats
path: root/src/wiki.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-11-04 11:30:10 +0100
committerDaniel Baumann <daniel@debian.org>2025-01-23 21:00:02 +0100
commit816c704d1586954492cd9cffab04652d4e5a6c03 (patch)
tree2f97789629e5de8597ee7cb59d386590ac6c6f05 /src/wiki.rs
parentInitial commit. (diff)
downloadforgejo-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.rs158
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(())
+}