use std::collections::BTreeMap;
use std::str::FromStr;
use super::Emitted;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueType {
Int,
Int64,
Uint64,
Float64,
Bool,
Duration,
}
impl FromStr for ValueType {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"int" => Ok(Self::Int),
"int64" => Ok(Self::Int64),
"uint64" => Ok(Self::Uint64),
"float64" => Ok(Self::Float64),
"bool" => Ok(Self::Bool),
"duration" => Ok(Self::Duration),
_ => Err(format!(
"unknown Go field type {value:?}; expected int, int64, uint64, float64, bool, or duration"
)),
}
}
}
impl ValueType {
pub(super) fn field_type(self, many: bool) -> String {
let scalar = match self {
Self::Int => "int",
Self::Int64 => "int64",
Self::Uint64 => "uint64",
Self::Float64 => "float64",
Self::Bool => "bool",
Self::Duration => "time.Duration",
};
if many {
format!("[]{scalar}")
} else {
scalar.to_string()
}
}
pub(super) fn converter(self) -> &'static str {
match self {
Self::Int => "NativeInt",
Self::Int64 => "Int",
Self::Uint64 => "Uint",
Self::Float64 => "Float",
Self::Bool => "Bool",
Self::Duration => "Duration",
}
}
}
pub(super) fn validate(
commands: &[Emitted],
types: &BTreeMap<String, ValueType>,
) -> Result<(), String> {
for key in types.keys() {
let flag = commands
.iter()
.flat_map(|e| &e.flags)
.find(|(_, named)| &named.key == key);
if let Some((flag, _)) = flag {
if flag.count || flag.arg.is_none() {
return Err(format!("{key} is not a value-taking flag"));
}
continue;
}
if commands
.iter()
.flat_map(|e| &e.args)
.any(|(_, named)| &named.key == key)
{
continue;
}
return Err(format!(
"{key} is not a generated command-level flag or argument key; clause fields are not supported yet"
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Spec;
use crate::go::{GoOptions, generate, generate_with_types};
#[test]
fn explicit_types_are_checked() {
let spec: Spec = "name \"ex\"\nflag \"--timeout <value>\"\nflag \"-v\" count=#true\nflag \"--force\"\narg \"[file]\"".parse().unwrap();
let opts = GoOptions::default();
assert_eq!(
generate(&spec, &opts),
generate_with_types(&spec, &opts, &BTreeMap::new()).unwrap()
);
for key in ["FlagMissing", "FlagV", "FlagForce", "CmdRoot"] {
assert!(
generate_with_types(
&spec,
&opts,
&BTreeMap::from([(key.to_string(), ValueType::Duration)])
)
.is_err(),
"{key}"
);
}
let out = generate_with_types(
&spec,
&opts,
&BTreeMap::from([("FlagTimeout".to_string(), ValueType::Duration)]),
)
.unwrap();
assert!(out.contains("time.Duration"));
assert!(out.contains("argv.Duration(\"--timeout\", values[len(values)-1])"));
assert!(!out.contains("out.Timeout = ev.Value"));
assert!("typo".parse::<ValueType>().is_err());
}
}