Skip to content

Commit

Permalink
refactor(cli): CLI rewrite (#284)
Browse files Browse the repository at this point in the history
* Rewrote the ClI to use derive and to be more expandable in the future

Signed-off-by: konrad <kokos.kekse@gmail.com>

* fix: Cli tests

Signed-off-by: konrad <kokos.kekse@gmail.com>

* fix: Removed anyhow from Cli so xtask can do its thing.

Signed-off-by: konrad <kokos.kekse@gmail.com>

* fix: Clippy naming convention

Signed-off-by: konrad <kokos.kekse@gmail.com>

---------

Signed-off-by: konrad <kokos.kekse@gmail.com>
  • Loading branch information
kokoISnoTarget authored Mar 23, 2024
1 parent 7693557 commit afa874d
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 132 deletions.
32 changes: 32 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ comrak = { version = "0.21.0", default-features = false, features = ["shortcodes
open = "5.1.2"
html5ever = "0.26.0"
image = "0.24.9"
clap = { version = "4.4.18", features = ["cargo"] }
clap = { version = "4.4.18", features = ["cargo", "derive"] }
copypasta = { version = "0.10.1", default-features = false }
resvg = "0.37.0"
anyhow = "1.0.81"
Expand Down Expand Up @@ -71,6 +71,7 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
glyphon = "0.3"
string_cache = { version = "0.8.7", default-features = false }
raw-window-handle = "0.5.2"
edit = "0.1.5"

[profile.release]
strip = true
Expand Down
39 changes: 23 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use image::{Image, ImageData};
use interpreter::HtmlInterpreter;
use keybindings::action::{Action, HistDirection, VertDirection, Zoom};
use keybindings::{Key, KeyCombos, ModifiedKey};
use opts::{Args, Config, Opts};
use opts::{Cli, Config, Opts};
use positioner::{Positioned, Row, Section, Spacer, DEFAULT_MARGIN, DEFAULT_PADDING};
use raw_window_handle::HasRawDisplayHandle;
use renderer::Renderer;
Expand All @@ -51,7 +51,9 @@ use tracing_subscriber::prelude::*;
use tracing_subscriber::util::SubscriberInitExt;
use utils::{ImageCache, Point, Rect, Size};

use crate::opts::Commands;
use anyhow::Context;
use clap::Parser;
use taffy::Taffy;
use winit::event::{
ElementState, Event, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, WindowEvent,
Expand Down Expand Up @@ -754,21 +756,26 @@ fn main() -> anyhow::Result<()> {
.with(tracing_subscriber::fmt::layer().compact())
.init();

let args = Args::new();
let config = match &args.config {
Some(config_path) => Config::load_from_file(config_path)?,
None => Config::load_from_system().unwrap_or_else(|err| {
tracing::warn!(
"Failed reading config file. Falling back to defaults. Error: {}",
err
);
Config::default()
}),
};
let opts = Opts::parse_and_load_from(args, config)?;

let inlyne = Inlyne::new(opts)?;
inlyne.run();
let command = Cli::parse().into_commands();

match command {
Commands::View(view) => {
let config = match &view.config {
Some(config_path) => Config::load_from_file(config_path)?,
None => Config::load_from_system().unwrap_or_else(|err| {
tracing::warn!(
"Failed reading config file. Falling back to defaults. Error: {}",
err
);
Config::default()
}),
};
let opts = Opts::parse_and_load_from(view, config)?;

let inlyne = Inlyne::new(opts)?;
inlyne.run();
}
}

Ok(())
}
144 changes: 52 additions & 92 deletions src/opts/cli.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;

use clap::builder::PossibleValue;
use clap::{command, value_parser, Arg, Command, ValueEnum, ValueHint};
use clap::{
builder::PossibleValue, command, value_parser, Args as ClapArgs, Parser, Subcommand, ValueEnum,
};
use serde::Deserialize;

const SCALE_HELP: &str =
"Factor to scale rendered file by [default: OS defined window scale factor]";
use std::path::PathBuf;

#[derive(Deserialize, Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ThemeType {
Expand Down Expand Up @@ -37,96 +32,61 @@ impl ValueEnum for ThemeType {
}
}

#[derive(Debug, PartialEq, Clone, Default)]
pub struct Args {
pub file_path: PathBuf,
pub theme: Option<ThemeType>,
pub scale: Option<f32>,
pub config: Option<PathBuf>,
pub page_width: Option<f32>,
#[derive(Debug, PartialEq, Clone, Parser)]
#[command(version, about, arg_required_else_help(true))]
#[clap(args_conflicts_with_subcommands = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Commands>,
#[command(flatten)]
pub view_file: Option<View>,
}

pub fn command() -> Command {
let file_arg = Arg::new("file")
.required(true)
.number_of_values(1)
.value_name("FILE")
.value_parser(value_parser!(PathBuf))
.value_hint(ValueHint::AnyPath)
.help("Path to the markdown file");

let theme_arg = Arg::new("theme")
.short('t')
.long("theme")
.number_of_values(1)
.value_parser(value_parser!(ThemeType))
.help("Theme to use when rendering");

let scale_arg = Arg::new("scale")
.short('s')
.long("scale")
.number_of_values(1)
.value_parser(value_parser!(f32))
.help(SCALE_HELP);

let config_arg = Arg::new("config")
.short('c')
.long("config")
.number_of_values(1)
.value_parser(value_parser!(PathBuf))
.help("Configuration file to use");

let page_width_arg = Arg::new("page_width")
.short('w')
.long("page-width")
.number_of_values(1)
.value_parser(value_parser!(f32))
.help("Maximum width of page in pixels");
impl Cli {
pub fn into_commands(self) -> Commands {
if let Some(view) = self.view_file {
Commands::View(view)
} else {
self.command.expect("Command should be Some!")
}
}
pub fn into_view(self) -> Result<View, &'static str> {
Ok(if let Some(view) = self.view_file {
view
} else if let Some(Commands::View(view)) = self.command {
view
} else {
return Err("Cli options do not contain an view option");
})
}
}

command!()
.arg(file_arg)
.arg(theme_arg)
.arg(scale_arg)
.arg(config_arg)
.arg(page_width_arg)
#[derive(Subcommand, Debug, PartialEq, Clone)]
pub enum Commands {
View(View),
}

impl Args {
pub fn new() -> Self {
let program_args = std::env::args_os().collect();
Self::parse_from(program_args)
}
/// View markdown a file with inlyne
#[derive(ClapArgs, PartialEq, Debug, Clone, Default)]
#[command(arg_required_else_help(true))]
pub struct View {
/// Path to the markdown file
#[arg(value_name = "FILE", required = true)]
pub file_path: PathBuf,

pub fn parse_from(args: Vec<OsString>) -> Self {
#[cfg(test)]
{
let _ = args;
panic!("Use `Args::try_parse_from()` in tests");
}
#[cfg(not(test))]
match Self::try_parse_from(args) {
Ok(args) => args,
// Expose clap error normally
Err(clap_err) => clap_err.exit(),
}
}
/// Theme to use when rendering
#[arg(short = 't', long = "theme", value_parser = value_parser!(ThemeType))]
pub theme: Option<ThemeType>,

pub fn try_parse_from(args: Vec<OsString>) -> Result<Self, clap::Error> {
let c = command();
let matches = c.try_get_matches_from(args)?;
/// Factor to scale rendered file by [default: OS defined window scale factor]
#[arg(short = 's', long = "scale")]
pub scale: Option<f32>,

let file_path = matches.get_one("file").cloned().unwrap();
let theme = matches.get_one("theme").cloned();
let scale = matches.get_one("scale").cloned();
let config = matches.get_one("config").cloned();
let page_width = matches.get_one("page_width").cloned();
/// Configuration file to use
#[arg(short = 'c', long = "config")]
pub config: Option<PathBuf>,

Ok(Self {
file_path,
theme,
scale,
config,
page_width,
})
}
/// Maximum width of page in pixels
#[arg(short = 'w', long = "page-width")]
pub page_width: Option<f32>,
}
15 changes: 8 additions & 7 deletions src/opts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ mod tests;
use std::path::Path;

use crate::color;
pub use cli::{Args, ThemeType};
pub use cli::{Cli, Commands, ThemeType, View};
pub use config::{Config, FontOptions, KeybindingsSection};

use crate::history::History;
use anyhow::Result;
use clap::Parser;
use serde::Deserialize;
use smart_debug::SmartDebug;

Expand Down Expand Up @@ -53,7 +54,7 @@ pub struct Opts {
}

impl Opts {
pub fn parse_and_load_from(args: Args, config: Config) -> Result<Self> {
pub fn parse_and_load_from(args: View, config: Config) -> Result<Self> {
#[cfg(test)]
{
// "Use" the unused params
Expand All @@ -69,15 +70,15 @@ impl Opts {

#[cfg(test)]
pub fn parse_and_load_with_system_theme(
args: Args,
args: View,
config: Config,
theme: Option<ResolvedTheme>,
) -> Result<Self> {
Self::parse_and_load_inner(args, config, theme)
}

fn parse_and_load_inner(
args: Args,
args: View,
config: Config,
fallback_theme: Option<ResolvedTheme>,
) -> Result<Self> {
Expand All @@ -92,7 +93,7 @@ impl Opts {
keybindings,
} = config;

let Args {
let View {
file_path,
theme: args_theme,
scale: args_scale,
Expand Down Expand Up @@ -135,11 +136,11 @@ impl Opts {

/// Arguments to supply to program that are opened externally.
pub fn program_args(file_path: &Path) -> Vec<String> {
let current_args = Args::new();
let current_args = Cli::parse().into_view().expect("Should contain an view!");

let mut args = Vec::new();

args.push(file_path.display().to_string());

if let Some(theme) = current_args.theme {
args.push("--theme".to_owned());
args.push(theme.as_str().to_owned());
Expand Down
Loading

0 comments on commit afa874d

Please sign in to comment.