summaryrefslogtreecommitdiffstats
path: root/src/wiki.rs
blob: 616789de9528fb0f96fb70ad4da1300a139489ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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())?;
        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(html_url.as_str())?;
    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(())
}