Merge pull request 'add optional fancy output' (#53) from optional-pretty into main

Reviewed-on: https://codeberg.org/Cyborus/forgejo-cli/pulls/53
This commit is contained in:
Cyborus 2024-05-07 19:24:09 +00:00
commit 0a30d14035
3 changed files with 140 additions and 10 deletions

View file

@ -1,3 +1,5 @@
use std::io::IsTerminal;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use eyre::{eyre, Context, OptionExt}; use eyre::{eyre, Context, OptionExt};
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
@ -14,6 +16,8 @@ mod repo;
pub struct App { pub struct App {
#[clap(long, short = 'H')] #[clap(long, short = 'H')]
host: Option<String>, host: Option<String>,
#[clap(long)]
style: Option<Style>,
#[clap(subcommand)] #[clap(subcommand)]
command: Command, command: Command,
} }
@ -36,6 +40,9 @@ pub enum Command {
#[tokio::main] #[tokio::main]
async fn main() -> eyre::Result<()> { async fn main() -> eyre::Result<()> {
let args = App::parse(); let args = App::parse();
let _ = SPECIAL_RENDER.set(SpecialRender::new(args.style.unwrap_or_default()));
let mut keys = KeyInfo::load().await?; let mut keys = KeyInfo::load().await?;
let host_name = args.host.as_deref(); let host_name = args.host.as_deref();
@ -137,3 +144,108 @@ async fn tempfile(ext: Option<&str>) -> tokio::io::Result<(tokio::fs::File, std:
.await?; .await?;
Ok((file, path)) Ok((file, path))
} }
use std::sync::OnceLock;
static SPECIAL_RENDER: OnceLock<SpecialRender> = OnceLock::new();
fn special_render() -> &'static SpecialRender {
SPECIAL_RENDER
.get()
.expect("attempted to get special characters before that was initialized")
}
#[derive(clap::ValueEnum, Clone, Copy, Debug, Default)]
enum Style {
/// Use special characters, and colors.
#[default]
Fancy,
/// No special characters and no colors. Always used in non-terminal contexts (i.e. pipes)
Minimal,
}
struct SpecialRender {
dash: char,
bullet: char,
body_prefix: char,
red: &'static str,
bright_red: &'static str,
green: &'static str,
bright_green: &'static str,
blue: &'static str,
bright_blue: &'static str,
cyan: &'static str,
bright_cyan: &'static str,
yellow: &'static str,
bright_yellow: &'static str,
magenta: &'static str,
bright_magenta: &'static str,
black: &'static str,
dark_grey: &'static str,
light_grey: &'static str,
white: &'static str,
reset: &'static str,
}
impl SpecialRender {
fn new(display: Style) -> Self {
let is_tty = std::io::stdout().is_terminal();
match display {
_ if !is_tty => Self::minimal(),
Style::Fancy => Self::fancy(),
Style::Minimal => Self::minimal(),
}
}
fn fancy() -> Self {
Self {
dash: '',
bullet: '',
body_prefix: '',
red: "\x1b[31m",
bright_red: "\x1b[91m",
green: "\x1b[32m",
bright_green: "\x1b[92m",
blue: "\x1b[34m",
bright_blue: "\x1b[94m",
cyan: "\x1b[36m",
bright_cyan: "\x1b[96m",
yellow: "\x1b[33m",
bright_yellow: "\x1b[93m",
magenta: "\x1b[35m",
bright_magenta: "\x1b[95m",
black: "\x1b[30m",
dark_grey: "\x1b[90m",
light_grey: "\x1b[37m",
white: "\x1b[97m",
reset: "\x1b[0m",
}
}
fn minimal() -> Self {
Self {
dash: '-',
bullet: '-',
body_prefix: '>',
red: "",
bright_red: "",
green: "",
bright_green: "",
blue: "",
bright_blue: "",
cyan: "",
bright_cyan: "",
yellow: "",
bright_yellow: "",
magenta: "",
bright_magenta: "",
black: "",
dark_grey: "",
light_grey: "",
white: "",
reset: "",
}
}
}

View file

@ -9,6 +9,7 @@ use tokio::io::AsyncWriteExt;
use crate::{ use crate::{
keys::KeyInfo, keys::KeyInfo,
repo::{RepoInfo, RepoName}, repo::{RepoInfo, RepoName},
SpecialRender,
}; };
#[derive(Args, Clone, Debug)] #[derive(Args, Clone, Debug)]
@ -374,6 +375,13 @@ async fn view_release(
&time::format_description::well_known::Rfc2822, &time::format_description::well_known::Rfc2822,
)?; )?;
println!(); println!();
let SpecialRender {
bullet,
body_prefix,
dark_grey,
reset,
..
} = crate::special_render();
let body = release let body = release
.body .body
.as_ref() .as_ref()
@ -381,7 +389,7 @@ async fn view_release(
if !body.is_empty() { if !body.is_empty() {
println!(); println!();
for line in body.lines() { for line in body.lines() {
println!("> {line}"); println!("{dark_grey}{body_prefix}{reset} {line}");
} }
println!(); println!();
} }
@ -396,10 +404,10 @@ async fn view_release(
.name .name
.as_ref() .as_ref()
.ok_or_else(|| eyre::eyre!("asset does not have name"))?; .ok_or_else(|| eyre::eyre!("asset does not have name"))?;
println!("- {}", name); println!("{bullet} {}", name);
} }
println!("- source.zip"); println!("{bullet} source.zip");
println!("- source.tar.gz"); println!("{bullet} source.tar.gz");
} }
Ok(()) Ok(())
} }

View file

@ -3,6 +3,8 @@ use eyre::{eyre, OptionExt};
use forgejo_api::structs::CreateRepoOption; use forgejo_api::structs::CreateRepoOption;
use url::Url; use url::Url;
use crate::SpecialRender;
pub struct RepoInfo { pub struct RepoInfo {
url: Url, url: Url,
name: Option<RepoName>, name: Option<RepoName>,
@ -351,6 +353,14 @@ impl RepoCommand {
.ok_or_eyre("couldn't get repo name, please specify")?; .ok_or_eyre("couldn't get repo name, please specify")?;
let repo = api.repo_get(repo.owner(), repo.name()).await?; let repo = api.repo_get(repo.owner(), repo.name()).await?;
let SpecialRender {
dash,
body_prefix,
dark_grey,
reset,
..
} = crate::special_render();
println!("{}", repo.full_name.ok_or_eyre("no full name")?); println!("{}", repo.full_name.ok_or_eyre("no full name")?);
if let Some(parent) = &repo.parent { if let Some(parent) = &repo.parent {
@ -370,7 +380,7 @@ impl RepoCommand {
println!(); println!();
} }
for line in desc.lines() { for line in desc.lines() {
println!("> {line}"); println!("{dark_grey}{body_prefix}{reset} {line}");
} }
} }
println!(); println!();
@ -382,13 +392,13 @@ impl RepoCommand {
let stars = repo.stars_count.unwrap_or_default(); let stars = repo.stars_count.unwrap_or_default();
if stars == 1 { if stars == 1 {
print!("{stars} star - "); print!("{stars} star {dash} ");
} else { } else {
print!("{stars} stars - "); print!("{stars} stars {dash} ");
} }
let watchers = repo.watchers_count.unwrap_or_default(); let watchers = repo.watchers_count.unwrap_or_default();
print!("{watchers} watching - "); print!("{watchers} watching {dash} ");
let forks = repo.forks_count.unwrap_or_default(); let forks = repo.forks_count.unwrap_or_default();
if forks == 1 { if forks == 1 {
@ -410,7 +420,7 @@ impl RepoCommand {
} }
if repo.has_pull_requests.unwrap_or_default() { if repo.has_pull_requests.unwrap_or_default() {
if !first { if !first {
print!(" - "); print!(" {dash} ");
} }
let pulls = repo.open_pr_counter.unwrap_or_default(); let pulls = repo.open_pr_counter.unwrap_or_default();
if pulls == 1 { if pulls == 1 {
@ -422,7 +432,7 @@ impl RepoCommand {
} }
if repo.has_releases.unwrap_or_default() { if repo.has_releases.unwrap_or_default() {
if !first { if !first {
print!(" - "); print!(" {dash} ");
} }
let releases = repo.release_counter.unwrap_or_default(); let releases = repo.release_counter.unwrap_or_default();
if releases == 1 { if releases == 1 {