#![allow(dead_code)]
use proptest::prelude::*;
pub fn arb_identifier() -> impl Strategy<Value = String> {
"[a-z][a-z0-9]{0,9}".prop_map(|s| s)
}
pub fn arb_short() -> impl Strategy<Value = Option<char>> {
prop_oneof![
Just(None),
any::<u32>().prop_map(|v| Some((b'a' + (v % 26) as u8) as char))
]
}
pub fn arb_description() -> impl Strategy<Value = String> {
"[a-zA-Z0-9 ]{1,30}"
}
pub fn arb_flag_def() -> impl Strategy<Value = (String, Option<char>, String)> {
(arb_identifier(), arb_short(), arb_description())
}
pub fn arb_option_def() -> impl Strategy<Value = (String, Option<char>, String, String, bool, Option<String>)> {
(
arb_identifier(),
arb_short(),
arb_identifier(),
arb_description(),
any::<bool>(),
prop_oneof![Just(None), arb_identifier().prop_map(Some),],
)
}
pub fn arb_positional_def() -> impl Strategy<Value = (String, String, bool)> {
(arb_identifier(), arb_description(), any::<bool>())
}
pub fn arb_safe_identifier() -> impl Strategy<Value = String> {
"[a-z][a-z0-9]{0,9}"
}
pub fn arb_safe_description() -> impl Strategy<Value = String> {
"[a-zA-Z0-9 ]{1,20}"
}
pub fn arb_safe_option_def() -> impl Strategy<Value = (String, Option<char>, String, String, bool, Option<String>)> {
(
arb_safe_identifier(),
arb_short(),
arb_safe_identifier(),
arb_safe_description(),
any::<bool>(),
prop_oneof![Just(None), arb_safe_identifier().prop_map(Some),],
)
}
pub fn arb_safe_positional_def() -> impl Strategy<Value = (String, String, bool)> {
(arb_safe_identifier(), arb_safe_description(), any::<bool>())
}
pub fn arb_safe_flag_def() -> impl Strategy<Value = (String, Option<char>, String)> {
(arb_safe_identifier(), arb_short(), arb_safe_description())
}
pub fn arb_subcommand_name() -> impl Strategy<Value = String> {
"[a-z][a-z0-9]{1,9}".prop_filter("avoid help collision", |s| s != "help")
}
pub fn arb_safe_subcommand_def() -> impl Strategy<
Value = (
String,
String,
Vec<(String, Option<char>, String)>,
Vec<(String, Option<char>, String, String, bool, Option<String>)>,
Vec<(String, String, bool)>,
),
> {
(
arb_subcommand_name(),
arb_safe_description(),
prop::collection::vec(arb_safe_flag_def(), 0..3),
prop::collection::vec(arb_safe_option_def(), 0..3),
prop::collection::vec(arb_safe_positional_def(), 0..3),
)
}
pub fn arb_flag_shorts(count: std::ops::RangeInclusive<usize>) -> impl Strategy<Value = Vec<char>> {
let chars: Vec<char> = ('a'..='z').filter(|&c| c != 'h').collect();
prop::collection::vec(prop::sample::select(chars), count)
.prop_map(|v| {
let mut seen = std::collections::HashSet::new();
v.into_iter().filter(|c| seen.insert(*c)).collect::<Vec<_>>()
})
.prop_filter("need at least 1 distinct flag char", |v| !v.is_empty())
}
pub fn arb_value_string() -> impl Strategy<Value = String> {
"[a-zA-Z0-9]{1,12}"
}
pub fn extract_section_lines<'a>(help: &'a str, header: &str) -> Vec<&'a str> {
let mut in_section = false;
let mut lines = Vec::new();
for line in help.lines() {
let plain = strip_ansi_inline(line);
if plain.trim() == header {
in_section = true;
continue;
}
if in_section {
if line.is_empty() || (!plain.starts_with(' ') && !line.is_empty()) {
break;
}
lines.push(line);
}
}
lines
}
pub fn strip_ansi_inline(s: &str) -> String {
let mut out = String::new();
let mut in_escape = false;
for ch in s.chars() {
if in_escape {
if ch.is_ascii_alphabetic() {
in_escape = false;
}
} else if ch == '\x1b' {
in_escape = true;
} else {
out.push(ch);
}
}
out
}
pub fn arb_env_var_name() -> impl Strategy<Value = String> {
"[A-Z][A-Z0-9_]{0,14}"
}
pub fn arb_u32_string() -> impl Strategy<Value = String> {
any::<u32>().prop_map(|n| n.to_string())
}
pub fn arb_non_numeric_string() -> impl Strategy<Value = String> {
"[a-zA-Z][a-zA-Z0-9]*".prop_filter("must not parse as u32", |s| s.parse::<u32>().is_err())
}
#[cfg(feature = "color")]
pub fn strip_ansi(s: &str) -> String {
let mut out = String::new();
let mut in_escape = false;
for ch in s.chars() {
if in_escape {
if ch.is_ascii_alphabetic() {
in_escape = false;
}
} else if ch == '\x1b' {
in_escape = true;
} else {
out.push(ch);
}
}
out
}
#[cfg(feature = "color")]
pub fn contains_ansi(s: &str) -> bool {
s.contains('\x1b')
}
#[cfg(feature = "color")]
pub fn contains_green(s: &str, text: &str) -> bool {
s.contains(&format!("\x1b[32m{text}\x1b[0m"))
}
#[cfg(feature = "color")]
pub fn contains_cyan(s: &str, text: &str) -> bool {
s.contains(&format!("\x1b[36m{text}\x1b[0m"))
}
#[cfg(feature = "color")]
pub fn contains_dim(s: &str, text: &str) -> bool {
s.contains(&format!("\x1b[2m{text}\x1b[0m"))
}
#[cfg(feature = "color")]
pub fn contains_bold_yellow(s: &str, text: &str) -> bool {
s.contains(&format!("\x1b[1;33m{text}\x1b[0m"))
}
#[cfg(feature = "color")]
pub fn contains_bold_red(s: &str, text: &str) -> bool {
s.contains(&format!("\x1b[1;31m{text}\x1b[0m"))
}
#[cfg(feature = "color")]
pub fn contains_yellow(s: &str, text: &str) -> bool {
s.contains(&format!("\x1b[33m{text}\x1b[0m"))
}