use std::io::{self, BufRead, IsTerminal};
use clap::parser::ValuesRef;
use clap::ArgMatches;
use crate::colorpicker::{print_colorspectrum, run_external_colorpicker};
use crate::config::Config;
use crate::{PastelError, Result};
use pastel::parser::parse_color;
use pastel::Color;
pub fn number_arg(matches: &ArgMatches, name: &str) -> Result<f64> {
let value_str = matches.get_one::<String>(name).expect("required argument");
value_str
.parse::<f64>()
.map_err(|_| PastelError::CouldNotParseNumber(value_str.into()))
}
#[derive(Debug, Clone, PartialEq)]
pub enum PrintSpectrum {
Yes,
No,
}
pub enum ColorArgIterator<'a> {
FromPositionalArguments(&'a Config<'a>, ValuesRef<'a, String>, PrintSpectrum),
FromStdin,
}
impl<'a> ColorArgIterator<'a> {
pub fn from_args(config: &'a Config, args: Option<ValuesRef<'a, String>>) -> Result<Self> {
match args {
Some(positionals) => Ok(ColorArgIterator::FromPositionalArguments(
config,
positionals,
PrintSpectrum::Yes,
)),
None => {
if io::stdin().is_terminal() {
return Err(PastelError::ColorArgRequired);
}
Ok(ColorArgIterator::FromStdin)
}
}
}
pub fn color_from_stdin() -> Result<Color> {
let stdin = io::stdin();
let mut lock = stdin.lock();
let mut line = String::new();
let size = lock
.read_line(&mut line)
.map_err(|_| PastelError::ColorInvalidUTF8)?;
if size == 0 {
return Err(PastelError::CouldNotReadFromStdin);
}
let line = line.trim();
parse_color(line).ok_or_else(|| PastelError::ColorParseError(line.to_string()))
}
pub fn from_color_arg(
config: &'a Config,
arg: &str,
print_spectrum: &mut PrintSpectrum,
) -> Result<Color> {
match arg {
"-" => Self::color_from_stdin(),
"pick" => {
if *print_spectrum == PrintSpectrum::Yes {
print_colorspectrum(config)?;
*print_spectrum = PrintSpectrum::No;
}
let color_str = run_external_colorpicker(config.colorpicker)?;
ColorArgIterator::from_color_arg(config, &color_str, print_spectrum)
}
color_str => {
parse_color(color_str).ok_or_else(|| PastelError::ColorParseError(color_str.into()))
}
}
}
}
impl Iterator for ColorArgIterator<'_> {
type Item = Result<Color>;
fn next(&mut self) -> Option<Self::Item> {
match self {
ColorArgIterator::FromPositionalArguments(
ref mut config,
ref mut args,
ref mut print_spectrum,
) => args
.next()
.map(|color_arg| Self::from_color_arg(config, color_arg, print_spectrum)),
ColorArgIterator::FromStdin => match Self::color_from_stdin() {
Ok(color) => Some(Ok(color)),
Err(PastelError::CouldNotReadFromStdin) => None,
err @ Err(_) => Some(err),
},
}
}
}