This is almost a complete re-write of the argument handling code, with everything being much more robust than before. This version is also much more idiomatic Xilem code than before, and hopefully that will continue to improve over time, with message-handling being the next obvious step.
MIY7QPYK3EZTOGZF5PWU2ZKM7NG5ANWKNHS56FVTOY7PPATWLSSAC G54GZBS464DFT2224SUTRWPRRSLUQKWDXY2YA7USW7UZ7VPXIHEAC C73UJ7ZYG4EE3YTK3N66GXPNWJHEBSRE4PDQBWMN6SKQ3U6ZYKXAC BMG4FSHNV54VXDHNUVGZOMXQJWLFSUF3M5NCN7GJETNIF3QTHELQC ZYNEMGAZXWHIWGNPB2RTYG3JWTH5Y5XY4JWJ3TTPANIOORTCLISAC JFJVY57RWKT6YJ62MWWOHXSLASLWPORFUGF67TVTW4FW7XBZEAUQC REI53XR4WDH5EKLTXTFVTOVWCCFRWTFH4GQS6DSNM5LSRFTX7HJQC A4E5KLI2CEHJLO6WUME2VIXPK4C2DXHCBVEK2TJTLHGCRCZ2ZC7QC }fn arg_item(matches: &ArgMatches, arg: &clap::Arg) -> impl View<AppState> + ViewMarker {tracing::debug!("`{:?}`", matches.contains_id("name"));let source = if let Some(source) = matches.value_source(arg.get_id().as_str()) {match source {ValueSource::DefaultValue => "default",ValueSource::EnvVariable => "environment",ValueSource::CommandLine => "cmdline",_ => "todo",}} else {"none"};let type_id = arg.get_value_parser().type_id();let type_name = if type_id == value_parser!(String).type_id() {"string".to_string()} else {format!("{:?}", type_id)};el::div((el::div((type_name, el::div("test").attr("class", "hover_content"))).attr("class", "hoverable"),el::div(source),el::ul(matches.get_raw_occurrences(&arg.get_id().as_str()).unwrap_or_default().map(|os_strings| {el::li({os_strings.map(|s| s.to_string_lossy().to_string()).collect::<Vec<String>>().join(" ")})}).collect::<Vec<_>>(),),))
el::div((el::div(MockApp::command().get_opts().filter(|arg| arg_matches.try_contains_id(arg.get_id().as_str()).is_ok()).map(|arg| arg_item(&arg_matches, arg)).collect::<Vec<_>>(),).attr("id", "sidebar"),)).attr("class", "container"),
el::br(()),arg::args_view(state),
use clap::parser::ValueSource;use clap::{value_parser, Arg};use xilem_html::{elements as el, Adapt, MessageResult, View, ViewMarker, ViewSequence};use crate::AppState;#[non_exhaustive]enum ArgType {String,Other,}impl From<&Arg> for ArgType {fn from(value: &Arg) -> Self {let type_id = value.get_value_parser().type_id();if type_id == value_parser!(String).type_id() {Self::String} else {Self::Other}}}fn type_description(arg: &Arg) -> impl View<Arg> + ViewMarker {let supported_type_description = ArgType::from(arg);el::div((match supported_type_description {ArgType::String => "string",ArgType::Other => "other",},el::div("placeholder content").attr("class", "hover_content"),)).attr("class", "hoverable")}fn env(arg: &Arg) -> impl ViewSequence<Arg> {arg.get_env().map(|variable| variable.to_string_lossy().to_string())}fn aliases(arg: &Arg) -> impl ViewSequence<Arg> {let aliases = if let Some(aliases) = arg.get_all_aliases() {aliases.iter().map(|alias| alias.to_string()).filter(|alias| alias != arg.get_id()).collect::<Vec<String>>()} else {// Ignore if there's no aliasesreturn None;};// Ignore if all aliases have been filtered out.// This will usually happen when there is 1 alias returned by get_all_aliases,// which is the long argument version, which is equivalent to the argument id// and is therefore redundantif aliases.is_empty() {return None;}Some(el::div((el::h4("Also known as:"),el::ul(aliases.into_iter().map(el::li).collect::<Vec<el::Li<_>>>()),)))}fn source(state: &AppState, arg: &Arg) -> impl View<Arg> + ViewMarker {String::from(if let Ok(arg_matches) = state.arg_matches.as_ref() {if let Some(source) = arg_matches.value_source(arg.get_id().as_str()) {match source {ValueSource::DefaultValue => "default",ValueSource::EnvVariable => "environment",ValueSource::CommandLine => "cmdline",_ => "todo",}} else {"none"}} else {"error"})}fn matched_values(state: &AppState, arg: &Arg) -> impl View<Arg> + ViewMarker {el::ul(state.arg_matches.as_ref().iter().map(|arg_matches| {arg_matches.get_raw_occurrences(&arg.get_id().as_str()).unwrap_or_default().map(|os_strings| {el::li(os_strings.map(|s| s.to_string_lossy().to_string()).collect::<Vec<String>>().join(" "),)})}).flatten().collect::<Vec<el::Li<_>>>(),)}fn item(state: &AppState, arg: &Arg) -> impl View<Arg> + ViewMarker {el::div((// Name of the argumentel::h3(arg.get_id().to_string().to_uppercase()),// The argument's type infotype_description(arg),el::br(()),// Alternate sourcesel::div((env(arg), aliases(arg))),// Hand-written documentationel::div((// One-line short helparg.get_help().map(|short_help| el::i(short_help.to_string())),el::br(()),// Long-form helparg.get_long_help().map(|long_help| el::q(long_help.to_string())),)),// Source location (defaults, command line etc)source(state, arg),// Number of allowed argumentsarg.get_num_args().map(|arg_range| arg_range.to_string()),// Matched valuesmatched_values(state, arg),))}pub fn args_view(state: &mut AppState) -> impl View<AppState> + ViewMarker {el::div(if state.arg_matches.is_ok() {state.command.get_opts().map(|arg| {Adapt::new(|_state, _thunk| {// TODO: actually re-compute this valueMessageResult::Nop},item(state, arg),)}).collect::<Vec<_>>()} else {Vec::new()}).attr("id", "sidebar")}