use std::io;
use clap::error::ErrorKind;
use clap::Parser;
use clap::{command, Arg, Args, FromArgMatches};
use clap_complete::{generate, Shell};
use clap_num::maybe_hex;
use crate::chromium_ec::commands::SetGpuSerialMagic;
use crate::chromium_ec::CrosEcDriverType;
use crate::commandline::{
Cli, ConsoleArg, FpBrightnessArg, HardwareDeviceType, InputDeckModeArg, LogLevel, RebootEcArg,
TabletModeArg,
};
#[derive(Parser)]
#[command(arg_required_else_help = true)]
struct ClapCli {
#[command(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
#[arg(long)]
versions: bool,
#[arg(long)]
version: bool,
#[arg(long)]
features: bool,
#[arg(long)]
esrt: bool,
#[clap(value_enum)]
#[arg(long)]
device: Option<HardwareDeviceType>,
#[arg(long)]
compare_version: Option<String>,
#[arg(long)]
power: bool,
#[arg(long)]
thermal: bool,
#[arg(long)]
sensors: bool,
#[clap(num_args=..=2)]
#[arg(long)]
fansetduty: Vec<u32>,
#[clap(num_args=..=2)]
#[arg(long)]
fansetrpm: Vec<u32>,
#[arg(long)]
autofanctrl: Option<Option<u8>>,
#[arg(long)]
pdports: bool,
#[arg(long)]
pdports_chromebook: bool,
#[arg(long)]
info: bool,
#[arg(long)]
meinfo: Option<Option<String>>,
#[arg(long)]
pd_info: bool,
#[arg(long)]
pd_reset: Option<u8>,
#[arg(long)]
pd_disable: Option<u8>,
#[arg(long)]
pd_enable: Option<u8>,
#[arg(long)]
dp_hdmi_info: bool,
#[arg(long, value_name = "UPDATE_BIN")]
dp_hdmi_update: Option<std::path::PathBuf>,
#[arg(long)]
audio_card_info: bool,
#[arg(long)]
privacy: bool,
#[arg(long)]
pd_bin: Option<std::path::PathBuf>,
#[arg(long)]
ec_bin: Option<std::path::PathBuf>,
#[arg(long)]
capsule: Option<std::path::PathBuf>,
#[arg(long)]
dump: Option<std::path::PathBuf>,
#[arg(long)]
h2o_capsule: Option<std::path::PathBuf>,
#[arg(long)]
dump_ec_flash: Option<std::path::PathBuf>,
#[arg(long)]
flash_full_ec: Option<std::path::PathBuf>,
#[arg(long)]
flash_ec: Option<std::path::PathBuf>,
#[arg(long)]
flash_ro_ec: Option<std::path::PathBuf>,
#[arg(long)]
flash_rw_ec: Option<std::path::PathBuf>,
#[arg(long)]
intrusion: bool,
#[arg(long)]
inputdeck: bool,
#[arg(long)]
inputdeck_mode: Option<InputDeckModeArg>,
#[arg(long)]
expansion_bay: bool,
#[arg(long)]
charge_limit: Option<Option<u8>>,
#[arg(long)]
#[clap(num_args = 1..=2)]
charge_current_limit: Vec<u32>,
#[arg(long)]
#[clap(num_args = 1..=2)]
charge_rate_limit: Vec<f32>,
#[arg(long)]
get_gpio: Option<Option<String>>,
#[arg(long)]
fp_led_level: Option<Option<FpBrightnessArg>>,
#[arg(long)]
fp_brightness: Option<Option<u8>>,
#[arg(long)]
kblight: Option<Option<u8>>,
#[arg(long, value_parser=maybe_hex::<u16>)]
#[clap(num_args = 3)]
remap_key: Vec<u16>,
#[clap(num_args = 2..)]
#[arg(long, value_parser=maybe_hex::<u64>, value_names(["START", "HEXCOLOR"]))]
rgbkbd: Vec<u64>,
#[clap(value_enum, hide(true))]
#[arg(long)]
ps2_enable: Option<bool>,
#[clap(value_enum)]
#[arg(long)]
tablet_mode: Option<TabletModeArg>,
#[clap(value_enum)]
#[arg(long)]
touchscreen_enable: Option<bool>,
#[clap(value_enum)]
#[arg(long)]
stylus_battery: bool,
#[clap(value_enum)]
#[arg(long)]
console: Option<ConsoleArg>,
#[clap(value_enum)]
#[arg(long)]
reboot_ec: Option<RebootEcArg>,
#[clap(value_enum)]
#[arg(long)]
ec_hib_delay: Option<Option<u32>>,
#[arg(long)]
uptimeinfo: bool,
#[arg(long)]
s0ix_counter: bool,
#[arg(long)]
hash: Option<std::path::PathBuf>,
#[clap(value_enum)]
#[arg(long)]
driver: Option<CrosEcDriverType>,
#[clap(number_of_values = 3, requires("pd_ports"))]
#[arg(long)]
pd_addrs: Vec<u16>,
#[clap(number_of_values = 3, requires("pd_addrs"))]
#[arg(long)]
pd_ports: Vec<u8>,
#[arg(long, short)]
test: bool,
#[arg(long)]
test_retimer: bool,
#[arg(long)]
boardid: bool,
#[arg(long, short)]
force: bool,
#[arg(long)]
dry_run: bool,
#[arg(long)]
flash_gpu_descriptor_file: Option<std::path::PathBuf>,
#[arg(long)]
dump_gpu_descriptor_file: Option<std::path::PathBuf>,
#[arg(long)]
nvidia: bool,
#[arg(long, value_parser=maybe_hex::<u16>)]
#[clap(num_args = 2..)]
host_command: Vec<u16>,
#[arg(long, value_name = "SHELL", hide = true)]
generate_completions: Option<Shell>,
}
pub fn parse(args: &[String]) -> Cli {
let cli = command!()
.arg(Arg::new("fgd").long("flash-gpu-descriptor").num_args(2))
.disable_version_flag(true);
let mut cli = ClapCli::augment_args(cli);
if let Some(shell_arg) = args.iter().position(|a| a == "--generate-completions") {
if let Some(shell_str) = args.get(shell_arg + 1) {
if let Ok(shell) = shell_str.parse::<Shell>() {
generate(shell, &mut cli, "framework_tool", &mut io::stdout());
std::process::exit(0);
}
}
}
let matches = cli.clone().get_matches_from(args);
let fgd = matches
.get_many::<String>("fgd")
.unwrap_or_default()
.map(|v| v.as_str())
.collect::<Vec<_>>();
let flash_gpu_descriptor = if !fgd.is_empty() {
let hex_magic = if let Some(hex_magic) = fgd[0].strip_prefix("0x") {
u8::from_str_radix(hex_magic, 16)
} else {
u8::from_str_radix("", 16)
};
let magic = if let Ok(magic) = fgd[0].parse::<u8>() {
magic
} else if let Ok(hex_magic) = hex_magic {
hex_magic
} else if fgd[0].to_uppercase() == "GPU" {
SetGpuSerialMagic::WriteGPUConfig as u8
} else if fgd[0].to_uppercase() == "SSD" {
SetGpuSerialMagic::WriteSSDConfig as u8
} else {
cli.error(
ErrorKind::InvalidValue,
"First argument of --flash-gpu-descriptor must be an integer or one of: 'GPU', 'SSD'",
)
.exit();
};
if fgd[1].len() != 18 {
cli.error(
ErrorKind::InvalidValue,
"Second argument of --flash-gpu-descriptor must be an 18 digit serial number",
)
.exit();
}
Some((magic, fgd[1].to_string()))
} else {
None
};
let args = ClapCli::from_arg_matches(&matches)
.map_err(|err| err.exit())
.unwrap();
let pd_addrs = match args.pd_addrs.len() {
3 => Some((args.pd_addrs[0], args.pd_addrs[1], args.pd_addrs[2])),
0 => None,
_ => unreachable!(),
};
let pd_ports = match args.pd_ports.len() {
3 => Some((args.pd_ports[0], args.pd_ports[1], args.pd_ports[2])),
0 => None,
_ => unreachable!(),
};
let fansetduty = match args.fansetduty.len() {
2 => Some((Some(args.fansetduty[0]), args.fansetduty[1])),
1 => Some((None, args.fansetduty[0])),
_ => None,
};
let fansetrpm = match args.fansetrpm.len() {
2 => Some((Some(args.fansetrpm[0]), args.fansetrpm[1])),
1 => Some((None, args.fansetrpm[0])),
_ => None,
};
let charge_current_limit = match args.charge_current_limit.len() {
2 => Some((
args.charge_current_limit[0],
Some(args.charge_current_limit[1]),
)),
1 => Some((args.charge_current_limit[0], None)),
_ => None,
};
let charge_rate_limit = match args.charge_rate_limit.len() {
2 => Some((args.charge_rate_limit[0], Some(args.charge_rate_limit[1]))),
1 => Some((args.charge_rate_limit[0], None)),
_ => None,
};
let remap_key = match args.remap_key.len() {
3 => Some((
args.remap_key[0] as u8,
args.remap_key[1] as u8,
args.remap_key[2],
)),
_ => None,
};
let host_command = if args.host_command.len() >= 2 {
let cmd_ver = if let Ok(cmd_ver) = u8::try_from(args.host_command[1]) {
cmd_ver
} else {
cli.error(
ErrorKind::InvalidValue,
"Second argument of --host-command must be a one byte command version",
)
.exit();
};
Some((
args.host_command[0],
cmd_ver,
args.host_command[2..]
.iter()
.map(|&x| {
if let Ok(x) = u8::try_from(x) {
x
} else {
cli.error(
ErrorKind::InvalidValue,
"All payload values of --host-command must be one byte each",
)
.exit();
}
})
.collect(),
))
} else {
None
};
Cli {
verbosity: LogLevel(args.verbosity.log_level_filter()),
versions: args.versions,
version: args.version,
features: args.features,
esrt: args.esrt,
device: args.device,
compare_version: args.compare_version,
power: args.power,
thermal: args.thermal,
sensors: args.sensors,
fansetduty,
fansetrpm,
autofanctrl: args.autofanctrl,
pdports: args.pdports,
pdports_chromebook: args.pdports_chromebook,
pd_info: args.pd_info,
pd_reset: args.pd_reset,
pd_disable: args.pd_disable,
pd_enable: args.pd_enable,
dp_hdmi_info: args.dp_hdmi_info,
dp_hdmi_update: args
.dp_hdmi_update
.map(|x| x.into_os_string().into_string().unwrap()),
audio_card_info: args.audio_card_info,
privacy: args.privacy,
pd_bin: args
.pd_bin
.map(|x| x.into_os_string().into_string().unwrap()),
ec_bin: args
.ec_bin
.map(|x| x.into_os_string().into_string().unwrap()),
capsule: args
.capsule
.map(|x| x.into_os_string().into_string().unwrap()),
dump: args.dump.map(|x| x.into_os_string().into_string().unwrap()),
h2o_capsule: args
.h2o_capsule
.map(|x| x.into_os_string().into_string().unwrap()),
dump_ec_flash: args
.dump_ec_flash
.map(|x| x.into_os_string().into_string().unwrap()),
flash_full_ec: args
.flash_full_ec
.map(|x| x.into_os_string().into_string().unwrap()),
flash_ec: args
.flash_ec
.map(|x| x.into_os_string().into_string().unwrap()),
flash_ro_ec: args
.flash_ro_ec
.map(|x| x.into_os_string().into_string().unwrap()),
flash_rw_ec: args
.flash_rw_ec
.map(|x| x.into_os_string().into_string().unwrap()),
intrusion: args.intrusion,
inputdeck: args.inputdeck,
inputdeck_mode: args.inputdeck_mode,
expansion_bay: args.expansion_bay,
charge_limit: args.charge_limit,
charge_current_limit,
charge_rate_limit,
get_gpio: args.get_gpio,
fp_led_level: args.fp_led_level,
fp_brightness: args.fp_brightness,
kblight: args.kblight,
remap_key,
rgbkbd: args.rgbkbd,
ps2_enable: args.ps2_enable,
tablet_mode: args.tablet_mode,
touchscreen_enable: args.touchscreen_enable,
stylus_battery: args.stylus_battery,
console: args.console,
reboot_ec: args.reboot_ec,
ec_hib_delay: args.ec_hib_delay,
uptimeinfo: args.uptimeinfo,
s0ix_counter: args.s0ix_counter,
hash: args.hash.map(|x| x.into_os_string().into_string().unwrap()),
driver: args.driver,
pd_addrs,
pd_ports,
test: args.test,
test_retimer: args.test_retimer,
boardid: args.boardid,
dry_run: args.dry_run,
force: args.force,
help: false,
allupdate: false,
paginate: false,
info: args.info,
meinfo: args.meinfo,
flash_gpu_descriptor,
flash_gpu_descriptor_file: args
.flash_gpu_descriptor_file
.map(|x| x.into_os_string().into_string().unwrap()),
dump_gpu_descriptor_file: args
.dump_gpu_descriptor_file
.map(|x| x.into_os_string().into_string().unwrap()),
nvidia: args.nvidia,
host_command,
}
}