use std::{
any::TypeId,
iter::Peekable,
ops::Range,
path::PathBuf,
sync::atomic::{AtomicBool, Ordering},
};
use crossterm::style::Color;
use crate::{
context::Handle,
data::Pass,
form::{self, FormId},
text::{Text, txt},
};
macro_rules! implDeref {
($type:ty, $target:ty $(, $($args:tt)+)?) => {
impl$(<$($args)+>)? std::ops::Deref for $type$(<$($args)+>)? {
type Target = $target;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl$(<$($args)+>)? std::ops::DerefMut for $type$(<$($args)+>)? {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
}
}
pub trait Parameter: Sized + 'static {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text>;
fn arg_name() -> Text {
txt!("[param]arg")
}
}
pub enum Flag<S: AsRef<str> = String> {
Word(S),
Blob(S),
}
impl Parameter for Flag {
fn from_args(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let arg = args.next()?;
if !arg.is_quoted {
if let Some(word) = arg.strip_prefix("--") {
Ok((
Flag::Word(word.to_string()),
Some(form::id_of!("param.flag")),
))
} else if let Some(blob_chars) = arg.strip_prefix("-") {
let mut blob = String::new();
for char in blob_chars.chars() {
if !blob.chars().any(|c| c == char) {
blob.push(char);
}
}
Ok((Flag::Blob(blob), Some(form::id_of!("param.flag"))))
} else {
Err(txt!("[param.info]Flag[]s must start with `-` or `--`"))
}
} else {
Err(txt!("Quoted arguments can't be [param.info]Flag[]s"))
}
}
fn arg_name() -> Text {
txt!("[param.flag]flag")
}
}
impl<S: AsRef<str>> Flag<S> {
pub fn as_word(self) -> Result<S, Text> {
match self {
Flag::Word(word) => Ok(word),
Flag::Blob(_) => Err(txt!(
"[param.info]Flag[] is of type [param.info]blob[], not [param.info]word"
)),
}
}
pub fn is_word(&self, word: &str) -> bool {
self.as_str().as_word().ok().is_some_and(|w| w == word)
}
pub fn as_blob(self) -> Result<S, Text> {
match self {
Flag::Blob(blob) => Ok(blob),
Flag::Word(_) => Err(txt!(
"[param.info]Flag[] is of type [param.info]word[], not [param.info]blob"
)),
}
}
pub fn has_blob(&self, blob: &str) -> bool {
self.as_str()
.as_blob()
.ok()
.is_some_and(|b| blob.chars().all(|char| b.chars().any(|c| c == char)))
}
pub fn word_from_list<const N: usize>(self, list: [&str; N]) -> Result<&str, Text> {
let word = self.as_word()?;
if let Some(word) = list.into_iter().find(|w| w == &word.as_ref()) {
Ok(word)
} else {
Err(txt!("Word not in list of valid options"))
}
}
pub fn as_str(&self) -> Flag<&str> {
match self {
Flag::Word(word) => Flag::Word(word.as_ref()),
Flag::Blob(blob) => Flag::Blob(blob.as_ref()),
}
}
}
pub struct Flags(pub Vec<Flag>);
impl Parameter for Flags {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let mut list = Vec::new();
while let Ok(flag) = args.next_as(pa) {
list.push(flag);
}
Ok((Self(list), Some(form::id_of!("param.flag"))))
}
fn arg_name() -> Text {
txt!("[param.flag]flags...")
}
}
impl Flags {
pub fn has_word(&self, word: &str) -> bool {
self.0
.iter()
.any(|flag| flag.as_str().as_word() == Ok(word))
}
pub fn has_blob(&self, blob: &str) -> bool {
blob.chars().all(|char| {
self.0
.iter()
.filter_map(|flag| flag.as_str().as_blob().ok())
.any(|blob| blob.chars().any(|c| c == char))
})
}
}
impl std::ops::Deref for Flags {
type Target = Vec<Flag>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for Flags {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Scope {
Local,
Global,
}
impl Parameter for Scope {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
if let Ok((flag, form)) = args.next_as_with_form::<Flag>(pa) {
if flag.is_word("global") {
Ok((Scope::Global, form))
} else {
Err(txt!("Invalid flag, it can only be [param.info]--global"))
}
} else {
Ok((Scope::Local, None))
}
}
fn arg_name() -> Text {
txt!("[param.flag]--global[param.punctuation]?")
}
}
impl<P: Parameter> Parameter for Option<P> {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
match args.next_as::<P>(pa) {
Ok(arg) => Ok((Some(arg), None)),
Err(err) if args.is_forming_param => Err(err),
Err(_) => Ok((None, None)),
}
}
fn arg_name() -> Text {
txt!("{}[param.punctuation]?", P::arg_name())
}
}
impl<P: Parameter> Parameter for Vec<P> {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let mut returns = Vec::new();
loop {
match args.next_as::<P>(pa) {
Ok(ret) => returns.push(ret),
Err(err) if args.is_forming_param => return Err(err),
Err(_) => break Ok((returns, None)),
}
}
}
fn arg_name() -> Text {
txt!("{}[param.punctuation]...", P::arg_name())
}
}
impl<const N: usize, P: Parameter> Parameter for [P; N] {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
use std::mem::MaybeUninit;
let mut returns = [const { MaybeUninit::uninit() }; N];
for r in returns.iter_mut() {
match args.next_as::<P>(pa) {
Ok(ret) => *r = MaybeUninit::new(ret),
Err(err) => return Err(err),
}
}
Ok((returns.map(|ret| unsafe { ret.assume_init() }), None))
}
fn arg_name() -> Text {
let punct = form::id_of!("param.punctuation");
txt!(
"{punct}[[{}{punct}; [param.count]{N}{punct}]]",
P::arg_name()
)
}
}
pub struct Between<const MIN: usize, const MAX: usize, P>(pub Vec<P>);
impl<const MIN: usize, const MAX: usize, P: Parameter> Parameter for Between<MIN, MAX, P> {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let mut returns = Vec::new();
for _ in 0..MAX {
match args.next_as::<P>(pa) {
Ok(ret) => returns.push(ret),
Err(err) if args.is_forming_param => return Err(err),
Err(_) if returns.len() >= MIN => return Ok((Self(returns), None)),
Err(err) => return Err(err),
}
}
if returns.len() >= MIN {
Ok((Self(returns), None))
} else {
Err(txt!(
"List needed at least [param.info]{MIN}[] elements, got only [a]{}",
returns.len()
))
}
}
fn arg_name() -> Text {
let punct = form::id_of!("param.punctuation");
let count = form::id_of!("param.count");
txt!(
"{punct}[[{}{punct}; {count}{MIN}{punct}..{count}{MAX}{punct}]]",
P::arg_name()
)
}
}
impl<const MIN: usize, const MAX: usize, P> std::ops::Deref for Between<MIN, MAX, P> {
type Target = Vec<P>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const MIN: usize, const MAX: usize, P> std::ops::DerefMut for Between<MIN, MAX, P> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Parameter for String {
fn from_args(_: &Pass, args: &mut Args) -> Result<(String, Option<FormId>), Text> {
Ok((args.next()?.to_string(), None))
}
fn arg_name() -> Text {
txt!("[param]arg")
}
}
pub struct Remainder(pub String);
impl Parameter for Remainder {
fn from_args(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let remainder: String = std::iter::from_fn(|| args.next().ok().map(|arg| arg.value))
.collect::<Vec<&str>>()
.join(" ");
if remainder.is_empty() {
Err(txt!("There are no more arguments"))
} else {
Ok((Self(remainder), None))
}
}
fn arg_name() -> Text {
txt!("[param]args")
}
}
implDeref!(Remainder, String);
impl Parameter for Handle {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let buffer_name = args.next()?.value;
if let Some(handle) = crate::context::windows()
.buffers(pa)
.into_iter()
.find(|handle| handle.read(pa).name() == buffer_name)
{
Ok((handle, Some(form::id_of!("param.path.open"))))
} else {
Err(txt!("No buffer called [a]{buffer_name}[] open"))
}
}
fn arg_name() -> Text {
txt!("[param]buffer")
}
}
pub struct OtherBuffer(pub Handle);
impl Parameter for OtherBuffer {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let handle = args.next_as::<Handle>(pa)?;
let cur_handle = crate::context::current_buffer(pa);
if cur_handle == handle {
Err(txt!("Argument can't be the current buffer"))
} else {
Ok((Self(handle), Some(form::id_of!("param.path.open"))))
}
}
fn arg_name() -> Text {
txt!("[param]buffer")
}
}
implDeref!(OtherBuffer, Handle);
pub struct ValidFilePath(pub PathBuf, bool);
impl Parameter for ValidFilePath {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let path = PathBuf::from(
shellexpand::full(args.next()?.value)
.map_err(|err| txt!("{err}"))?
.into_owned(),
);
let canon_path = path.canonicalize();
let path = if let Ok(path) = &canon_path {
if !path.is_file() {
return Err(txt!("Path is not a buffer"));
}
path.clone()
} else if canon_path.is_err()
&& let Ok(canon_path) = path.with_file_name(".").canonicalize()
{
canon_path.join(
path.file_name()
.ok_or_else(|| txt!("Path has no buffer name"))?,
)
} else {
return Err(txt!("Path was not found"));
};
if let Some(parent) = path.parent()
&& let Ok(false) | Err(_) = parent.try_exists()
{
return Err(txt!("Path's parent doesn't exist"));
}
let (form, exists_or_is_open) = if crate::context::windows()
.buffers(pa)
.into_iter()
.map(|handle| handle.read(pa).path())
.any(|p| std::path::Path::new(&p) == path)
{
(form::id_of!("param.path.open"), true)
} else if let Ok(true) = path.try_exists() {
(form::id_of!("param.path.exists"), true)
} else {
(form::id_of!("param.path"), false)
};
Ok((Self(path, exists_or_is_open), Some(form)))
}
fn arg_name() -> Text {
txt!("[param]path")
}
}
implDeref!(ValidFilePath, PathBuf);
#[doc(hidden)]
pub enum PathOrBufferOrCfg {
Path(PathBuf),
Buffer(Handle),
Cfg,
CfgManifest,
}
impl Parameter for PathOrBufferOrCfg {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
struct DropGuard;
impl Drop for DropGuard {
fn drop(&mut self) {
ONLY_EXISTING.store(false, Ordering::Relaxed);
}
}
let _guard = DropGuard;
args.use_completions_for::<CfgOrManifest>();
args.use_completions_for::<Handle>();
args.use_completions_for::<ValidFilePath>();
if let Ok((cfg_or_manifest, form)) = args.next_as_with_form::<CfgOrManifest>(pa) {
match cfg_or_manifest {
CfgOrManifest::Cfg => Ok((Self::Cfg, form)),
CfgOrManifest::Manifest => Ok((Self::CfgManifest, form)),
}
} else if let Ok((handle, form)) = args.next_as_with_form::<Handle>(pa) {
Ok((Self::Buffer(handle), form))
} else {
let (path, form) = args.next_as_with_form::<ValidFilePath>(pa)?;
if !path.1 && ONLY_EXISTING.load(Ordering::Relaxed) {
Err(txt!("[a]{path}[]: No such file"))
} else {
Ok((Self::Path(path.0), form))
}
}
}
fn arg_name() -> Text {
let flag = form::id_of!("param.flag");
let punct = form::id_of!("param.punctuation");
txt!("[param]path{punct}/[param]buffer{punct}/{flag}--cfg{punct}/{flag}--cfg-manifest")
}
}
static ONLY_EXISTING: AtomicBool = AtomicBool::new(false);
pub struct Existing;
impl Parameter for Existing {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let initial = args.clone();
let Ok((flag, form)) = args.next_as_with_form::<Flag>(pa) else {
return Ok((Self, None));
};
if flag.is_word("existing") {
ONLY_EXISTING.store(true, Ordering::Relaxed);
} else {
*args = initial;
}
Ok((Self, form))
}
fn arg_name() -> Text {
txt!("[param.flag]--existing[param.punctuation]?")
}
}
pub enum CfgOrManifest {
Cfg,
Manifest,
}
impl Parameter for CfgOrManifest {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
if let Ok((flag, form)) = args.next_as_with_form::<Flag>(pa)
&& let Some(ret) = match flag.as_word()?.as_str() {
"cfg" => Some((Self::Cfg, form)),
"cfg-manifest" => Some((Self::Manifest, form)),
_ => None,
}
{
Ok(ret)
} else {
Err(txt!(
"Invalid flag, pick [param.flag]cfg[] or [param.flag]cfg-manifest"
))
}
}
fn arg_name() -> Text {
txt!("[param.flag]--cfg[param.punctuation]/[param.flag]--cfg-manifest")
}
}
pub struct F32PercentOfU8(pub f32);
impl Parameter for F32PercentOfU8 {
fn from_args(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let arg = args.next()?;
if let Some(percentage) = arg.strip_suffix("%") {
let percentage: u8 = percentage
.parse()
.map_err(|_| txt!("[a]{arg}[] is not a valid percentage"))?;
if percentage <= 100 {
Ok((Self(percentage as f32 / 100.0), None))
} else {
Err(txt!("[a]{arg}[] is more than [a]100%"))
}
} else {
let byte: u8 = arg
.parse()
.map_err(|_| txt!("[a]{arg}[] couldn't be parsed"))?;
Ok((Self(byte as f32 / 255.0), None))
}
}
fn arg_name() -> Text {
let punct = form::id_of!("param.punctuation");
txt!("[param]u8{punct}/[param]0..=100%")
}
}
implDeref!(F32PercentOfU8, f32);
impl Parameter for Color {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
const fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
t = if t < 0.0 { t + 1.0 } else { t };
t = if t > 1.0 { t - 1.0 } else { t };
if t < 1.0 / 6.0 {
p + (q - p) * 6.0 * t
} else if t < 1.0 / 2.0 {
q
} else if t < 2.0 / 3.0 {
p + (q - p) * (2.0 / 3.0 - t) * 6.0
} else {
p
}
}
let arg = args.next()?.value;
if let Some(hex) = arg.strip_prefix("#") {
let total = match u32::from_str_radix(hex, 16) {
Ok(total) if hex.len() == 6 => total,
_ => return Err(txt!("Hexcode does not contain 6 hex values")),
};
let r = (total >> 16) as u8;
let g = (total >> 8) as u8;
let b = total as u8;
Ok((Color::Rgb { r, g, b }, None))
} else if arg == "hsl" {
let hue = args.next_as::<F32PercentOfU8>(pa)?.0;
let sat = args.next_as::<F32PercentOfU8>(pa)?.0;
let lit = args.next_as::<F32PercentOfU8>(pa)?.0;
let [r, g, b] = if sat == 0.0 {
[lit.round() as u8; 3]
} else {
let q = if lit < 0.5 {
lit * (1.0 + sat)
} else {
lit + sat - lit * sat
};
let p = 2.0 * lit - q;
let r = hue_to_rgb(p, q, hue + 1.0 / 3.0);
let g = hue_to_rgb(p, q, hue);
let b = hue_to_rgb(p, q, hue - 1.0 / 3.0);
[r.round() as u8, g.round() as u8, b.round() as u8]
};
Ok((Color::Rgb { r, g, b }, None))
} else {
Err(txt!("Color format was not recognized"))
}
}
fn arg_name() -> Text {
txt!("[param]#{{rgb hex}}[param.punctuation]/[param]hsl {{h}} {{s}} {{l}}")
}
}
pub struct FormName(pub String);
impl Parameter for FormName {
fn from_args(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let arg = args.next()?.value;
if !arg.chars().all(|c| c.is_ascii_alphanumeric() || c == '.') {
return Err(txt!(
"Expected identifiers separated by '.'s, found [a]{arg}"
));
}
if crate::form::exists(arg) {
Ok((Self(arg.to_string()), Some(form::id_of_non_static(arg))))
} else {
Err(txt!("The form [a]{arg}[] has not been set"))
}
}
fn arg_name() -> Text {
txt!("[param]form")
}
}
implDeref!(FormName, String);
pub struct ColorSchemeArg(pub String);
impl Parameter for ColorSchemeArg {
fn from_args(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let scheme = args.next()?.value;
if crate::form::colorscheme_exists(scheme) {
Ok((ColorSchemeArg(scheme.to_string()), None))
} else {
Err(txt!("The colorscheme [a]{scheme}[] was not found"))
}
}
fn arg_name() -> Text {
txt!("[param]colorscheme")
}
}
implDeref!(ColorSchemeArg, String);
#[doc(hidden)]
pub struct ReloadOptions {
pub clean: bool,
pub update: bool,
}
impl Parameter for ReloadOptions {
fn from_args(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let flags = args.next_as::<Flags>(pa)?;
if flags
.iter()
.any(|flag| !flag.is_word("update") && !flag.is_word("clean"))
{
Err(txt!("Invalid [a]Flag"))
} else {
Ok((
Self {
clean: flags.has_word("clean"),
update: flags.has_word("update"),
},
Some(form::id_of!("param.flag")),
))
}
}
fn arg_name() -> Text {
let punct = form::id_of!("param.punctuation");
txt!("[param.flag]--clean{punct}? [param.flag]--update{punct}?")
}
}
#[derive(Clone)]
pub struct Args<'a> {
iter: Peekable<ArgsIter<'a>>,
param_range: Range<usize>,
has_to_start_param: bool,
is_forming_param: bool,
arg_n: usize,
currently_parsing: Vec<TypeId>,
last_parsed: Vec<(usize, TypeId)>,
}
impl<'arg> Args<'arg> {
pub(super) fn new(command: &'arg str) -> Self {
Self {
iter: ArgsIter::new(command).peekable(),
param_range: 0..0,
has_to_start_param: false,
is_forming_param: false,
arg_n: 0,
currently_parsing: Vec::new(),
last_parsed: Vec::new(),
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Arg<'arg>, Text> {
match self.iter.next() {
Some((value, range, is_quoted)) => {
self.param_range = range.clone();
if self.has_to_start_param {
self.has_to_start_param = false;
self.is_forming_param = true;
}
self.last_parsed.retain(|(start_n, ty)| {
self.currently_parsing.contains(ty) || self.arg_n == *start_n
});
self.arg_n += 1;
Ok(Arg { value, is_quoted })
}
None => Err(txt!("Wrong argument count")),
}
}
pub fn next_as<P: Parameter>(&mut self, pa: &Pass) -> Result<P, Text> {
self.next_as_with_form(pa).map(|(param, _)| param)
}
pub fn next_as_with_form<P: Parameter>(
&mut self,
pa: &Pass,
) -> Result<(P, Option<FormId>), Text> {
self.currently_parsing.push(TypeId::of::<P>());
let initial = (
self.iter.clone(),
self.param_range.clone(),
self.has_to_start_param,
self.is_forming_param,
self.arg_n,
);
self.has_to_start_param = true;
let ret = P::from_args(pa, self);
if ret.is_ok() {
self.is_forming_param = false;
} else {
self.iter = initial.0;
self.param_range = initial.1;
self.has_to_start_param = initial.2;
self.is_forming_param = initial.3;
self.arg_n = initial.4;
}
self.currently_parsing.retain(|&ty| ty != TypeId::of::<P>());
ret
}
pub fn next_else<T: Into<Text>>(&mut self, to_text: T) -> Result<&'arg str, Text> {
match self.next() {
Ok(arg) => Ok(arg.value),
Err(_) => Err(to_text.into()),
}
}
pub fn next_start(&mut self) -> Option<usize> {
self.iter.peek().map(|(_, r, _)| r.start)
}
pub fn param_range(&self) -> Range<usize> {
self.param_range.clone()
}
pub fn last_parsed(&self) -> Vec<TypeId> {
self.last_parsed.iter().map(|(_, ty)| *ty).collect()
}
pub fn use_completions_for<P: Parameter>(&mut self) {
self.last_parsed.push((self.arg_n, TypeId::of::<P>()));
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Arg<'arg> {
pub value: &'arg str,
pub is_quoted: bool,
}
impl<'arg> std::ops::Deref for Arg<'arg> {
type Target = &'arg str;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<'arg, 'other> PartialEq<&'other str> for Arg<'arg> {
fn eq(&self, other: &&'other str) -> bool {
&self.value == other
}
}
#[derive(Clone)]
pub struct ArgsIter<'a> {
command: &'a str,
chars: std::str::CharIndices<'a>,
start: Option<usize>,
end: Option<usize>,
is_quoting: bool,
last_char: char,
}
impl<'a> ArgsIter<'a> {
pub fn new(command: &'a str) -> Self {
let mut args_iter = Self {
command,
chars: command.char_indices(),
start: None,
end: None,
is_quoting: false,
last_char: 'a',
};
args_iter.next();
args_iter
}
}
impl<'a> Iterator for ArgsIter<'a> {
type Item = (&'a str, Range<usize>, bool);
fn next(&mut self) -> Option<Self::Item> {
let mut is_quoted = false;
while let Some((b, char)) = self.chars.next() {
let lc = self.last_char;
self.last_char = char;
if self.start.is_some() && char.is_whitespace() && !self.is_quoting {
self.end = Some(b);
break;
} else if char == '\'' && lc != '\\' {
self.is_quoting = !self.is_quoting;
if !self.is_quoting {
is_quoted = true;
self.end = Some(b);
break;
} else {
self.start = Some(b + 1);
}
} else if !char.is_whitespace() && self.start.is_none() {
self.start = Some(b);
}
}
let e = self.end.take().unwrap_or(self.command.len());
self.start
.take()
.map(|s| (&self.command[s..e], s..e, is_quoted))
}
}
macro_rules! parse_impl {
($t:ty, $static_list:expr) => {
impl Parameter for $t {
fn from_args(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
let arg = args.next()?;
let arg = arg.parse().map_err(|_| {
txt!(
"[a]{arg}[] couldn't be parsed as [param.info]{}[]",
stringify!($t)
)
});
arg.map(|arg| (arg, None))
}
fn arg_name() -> Text {
txt!("[param]{}", stringify!($t))
}
}
};
}
parse_impl!(bool, Some(&["true", "false"]));
parse_impl!(u8, None);
parse_impl!(u16, None);
parse_impl!(u32, None);
parse_impl!(u64, None);
parse_impl!(u128, None);
parse_impl!(usize, None);
parse_impl!(i8, None);
parse_impl!(i16, None);
parse_impl!(i32, None);
parse_impl!(i64, None);
parse_impl!(i128, None);
parse_impl!(isize, None);
parse_impl!(f32, None);
parse_impl!(f64, None);
parse_impl!(path, Some(&[]));
#[allow(non_camel_case_types)]
type path = std::path::PathBuf;