generated from PlexSheep/baserepo
implement a cli module #85
|
@ -48,7 +48,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// if the user requested the help, print in blue, otherwise in red as it's just an
|
// if the user requested the help, print in blue, otherwise in red as it's just an
|
||||||
// error
|
// error
|
||||||
if let libpt_cli::repl::error::ReplError::Parsing(e) = &e {
|
if let libpt_cli::repl::error::Error::Parsing(e) = &e {
|
||||||
if e.kind() == clap::error::ErrorKind::DisplayHelp {
|
if e.kind() == clap::error::ErrorKind::DisplayHelp {
|
||||||
println!("{}", style(e).cyan());
|
println!("{}", style(e).cyan());
|
||||||
continue;
|
continue;
|
||||||
|
@ -76,7 +76,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
if !fancy {
|
if !fancy {
|
||||||
println!("{}", text.join(" "))
|
println!("{}", text.join(" "))
|
||||||
} else {
|
} else {
|
||||||
printing::blockprint(text.join(" "), console::Color::Cyan)
|
printing::blockprint(&text.join(" "), console::Color::Cyan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,6 @@ Author: {author-with-newline}
|
||||||
/// # use libpt_cli::args::VerbosityLevel;
|
/// # use libpt_cli::args::VerbosityLevel;
|
||||||
/// use libpt_log::Level;
|
/// use libpt_log::Level;
|
||||||
/// # use clap::Parser;
|
/// # use clap::Parser;
|
||||||
/// use log;
|
|
||||||
///
|
///
|
||||||
/// # #[derive(Parser, Debug)]
|
/// # #[derive(Parser, Debug)]
|
||||||
/// # pub struct Opts {
|
/// # pub struct Opts {
|
||||||
|
@ -94,8 +93,6 @@ Author: {author-with-newline}
|
||||||
/// // Level might be None if the user wants no output at all.
|
/// // Level might be None if the user wants no output at all.
|
||||||
/// // for the 'tracing' level:
|
/// // for the 'tracing' level:
|
||||||
/// let level: Level = opts.verbose.level();
|
/// let level: Level = opts.verbose.level();
|
||||||
/// // for the 'log' level:
|
|
||||||
/// let llevel: log::Level = opts.verbose.level_for_log_crate();
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Parser, Clone, PartialEq, Eq, Hash)]
|
#[derive(Parser, Clone, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -45,10 +45,11 @@ use console::{style, Color};
|
||||||
/// use libpt_cli::console::Color;
|
/// use libpt_cli::console::Color;
|
||||||
/// use libpt_cli::printing::blockprint;
|
/// use libpt_cli::printing::blockprint;
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// blockprint("Hello world!".to_string(), Color::Blue);
|
/// blockprint("Hello world!", Color::Blue);
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // we just take an impl, using a &impl is much less ergonomic
|
||||||
pub fn blockprint(content: impl ToString, color: Color) {
|
pub fn blockprint(content: impl ToString, color: Color) {
|
||||||
println!("{}", blockfmt(content, color));
|
println!("{}", blockfmt(content, color));
|
||||||
}
|
}
|
||||||
|
@ -66,11 +67,12 @@ pub fn blockprint(content: impl ToString, color: Color) {
|
||||||
/// use libpt_cli::console::Color;
|
/// use libpt_cli::console::Color;
|
||||||
/// use libpt_cli::printing::blockfmt;
|
/// use libpt_cli::printing::blockfmt;
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// let formatted_content = blockfmt("Hello world!".to_string(), Color::Blue);
|
/// let formatted_content = blockfmt("Hello world!", Color::Blue);
|
||||||
/// println!("{}", formatted_content);
|
/// println!("{}", formatted_content);
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // we just take an impl, using a &impl is much less ergonomic
|
||||||
pub fn blockfmt(content: impl ToString, color: Color) -> String {
|
pub fn blockfmt(content: impl ToString, color: Color) -> String {
|
||||||
blockfmt_advanced(
|
blockfmt_advanced(
|
||||||
content,
|
content,
|
||||||
|
@ -98,7 +100,7 @@ pub fn blockfmt(content: impl ToString, color: Color) -> String {
|
||||||
/// println!(
|
/// println!(
|
||||||
/// "{}",
|
/// "{}",
|
||||||
/// blockfmt_advanced(
|
/// blockfmt_advanced(
|
||||||
/// "Hello world!".to_string(),
|
/// "Hello world!",
|
||||||
/// Some(Color::Blue),
|
/// Some(Color::Blue),
|
||||||
/// presets::UTF8_FULL,
|
/// presets::UTF8_FULL,
|
||||||
/// ContentArrangement::DynamicFullWidth,
|
/// ContentArrangement::DynamicFullWidth,
|
||||||
|
@ -120,6 +122,8 @@ pub fn blockfmt(content: impl ToString, color: Color) -> String {
|
||||||
/// - `preset`: The preset style for the border
|
/// - `preset`: The preset style for the border
|
||||||
/// - `arrangement`: The arrangement of the the border (e.g., stretch to sides, wrap around )
|
/// - `arrangement`: The arrangement of the the border (e.g., stretch to sides, wrap around )
|
||||||
/// - `alignment`: The alignment of the content within the cells (e.g., left, center, right)
|
/// - `alignment`: The alignment of the content within the cells (e.g., left, center, right)
|
||||||
|
#[allow(clippy::missing_panics_doc)] // we add a row then unwrap it, no panic should be possible
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // we just take an impl, using a &impl is much less ergonomic
|
||||||
pub fn blockfmt_advanced(
|
pub fn blockfmt_advanced(
|
||||||
content: impl ToString,
|
content: impl ToString,
|
||||||
color: Option<Color>,
|
color: Option<Color>,
|
||||||
|
|
|
@ -56,11 +56,10 @@ use libpt_log::trace;
|
||||||
#[embed_doc_image("repl_screenshot", "data/media/repl.png")]
|
#[embed_doc_image("repl_screenshot", "data/media/repl.png")]
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(multicall = true, help_template = REPL_HELP_TEMPLATE)]
|
#[command(multicall = true, help_template = REPL_HELP_TEMPLATE)]
|
||||||
|
#[allow(clippy::module_name_repetitions)] // we can't just name it `Default`, that's part of std
|
||||||
pub struct DefaultRepl<C>
|
pub struct DefaultRepl<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
/// the command you want to execute, along with its arguments
|
/// the command you want to execute, along with its arguments
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
@ -81,18 +80,14 @@ where
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
struct DefaultReplCompletion<C>
|
struct DefaultReplCompletion<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
commands: std::marker::PhantomData<C>,
|
commands: std::marker::PhantomData<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Repl<C> for DefaultRepl<C>
|
impl<C> Repl<C> for DefaultRepl<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -106,7 +101,7 @@ where
|
||||||
fn command(&self) -> &Option<C> {
|
fn command(&self) -> &Option<C> {
|
||||||
&self.command
|
&self.command
|
||||||
}
|
}
|
||||||
fn step(&mut self) -> Result<(), super::error::ReplError> {
|
fn step(&mut self) -> Result<(), super::error::Error> {
|
||||||
self.buf.clear();
|
self.buf.clear();
|
||||||
|
|
||||||
// NOTE: display::Input requires some kind of lifetime that would be a bother to store in
|
// NOTE: display::Input requires some kind of lifetime that would be a bother to store in
|
||||||
|
@ -139,9 +134,7 @@ where
|
||||||
|
|
||||||
impl<C> Default for DefaultRepl<C>
|
impl<C> Default for DefaultRepl<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
|
@ -150,9 +143,7 @@ where
|
||||||
|
|
||||||
impl<C> Debug for DefaultRepl<C>
|
impl<C> Debug for DefaultRepl<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("DefaultRepl")
|
f.debug_struct("DefaultRepl")
|
||||||
|
@ -167,16 +158,15 @@ where
|
||||||
|
|
||||||
impl<C> DefaultReplCompletion<C>
|
impl<C> DefaultReplCompletion<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
pub fn new() -> Self {
|
/// Make a new [`DefaultReplCompletion`] for the type `C`
|
||||||
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
commands: std::marker::PhantomData::<C>,
|
commands: std::marker::PhantomData::<C>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn commands(&self) -> Vec<String> {
|
fn commands() -> Vec<String> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
// every crate has the help command, but it is not part of the enum
|
// every crate has the help command, but it is not part of the enum
|
||||||
buf.push("help".to_string());
|
buf.push("help".to_string());
|
||||||
|
@ -199,9 +189,7 @@ where
|
||||||
|
|
||||||
impl<C> Default for DefaultReplCompletion<C>
|
impl<C> Default for DefaultReplCompletion<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
|
@ -210,14 +198,11 @@ where
|
||||||
|
|
||||||
impl<C> Completion for DefaultReplCompletion<C>
|
impl<C> Completion for DefaultReplCompletion<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
/// Simple completion implementation based on substring
|
/// Simple completion implementation based on substring
|
||||||
fn get(&self, input: &str) -> Option<String> {
|
fn get(&self, input: &str) -> Option<String> {
|
||||||
let matches = self
|
let matches = Self::commands()
|
||||||
.commands()
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|option| option.starts_with(input))
|
.filter(|option| option.starts_with(input))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ReplError {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Parsing(#[from] clap::Error),
|
Parsing(#[from] clap::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Input(#[from] dialoguer::Error),
|
Input(#[from] dialoguer::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
Other(#[from] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
use error::ReplError;
|
use error::Error;
|
||||||
mod default;
|
mod default;
|
||||||
pub use default::*;
|
pub use default::*;
|
||||||
|
|
||||||
|
@ -25,9 +25,7 @@ use clap::{Parser, Subcommand};
|
||||||
/// Unless you want to implement custom features (not just commands), just use [`DefaultRepl`].
|
/// Unless you want to implement custom features (not just commands), just use [`DefaultRepl`].
|
||||||
pub trait Repl<C>: Parser + Debug
|
pub trait Repl<C>: Parser + Debug
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug + Subcommand + strum::IntoEnumIterator,
|
||||||
C: Subcommand,
|
|
||||||
C: strum::IntoEnumIterator,
|
|
||||||
{
|
{
|
||||||
/// create a new repl
|
/// create a new repl
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
|
@ -40,5 +38,11 @@ where
|
||||||
/// This should be used at the start of your loop.
|
/// This should be used at the start of your loop.
|
||||||
///
|
///
|
||||||
/// Note that the help menu is an Error: [`clap::error::ErrorKind::DisplayHelp`]
|
/// Note that the help menu is an Error: [`clap::error::ErrorKind::DisplayHelp`]
|
||||||
fn step(&mut self) -> Result<(), ReplError>;
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// * [`Error::Input`] – [dialoguer] User Input had some kind of I/O Error
|
||||||
|
/// * [`Error::Parsing`] – [clap] could not parse the user input, or user requested help
|
||||||
|
/// * [`Error::Other`] – Any other error with [anyhow], [`DefaultRepl`] does not use this but custom implementations might
|
||||||
|
fn step(&mut self) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue