use {
super::{bracketed, map_err, trim_spaces, OResult, Value, ValueParser},
nom::{
branch::alt,
bytes::complete::{is_not, tag, take},
character::complete::{char, space1, u8},
combinator::{all_consuming, map, map_parser, map_res, opt, rest},
multi::{fold_many0, many0, separated_list0},
sequence::{separated_pair, tuple},
Finish, IResult,
},
std::{borrow::Cow, fmt, io, str},
};
#[derive(Clone, PartialEq, Eq)]
pub struct VcpValue {
pub value: u8,
pub sub_values: Option<Vec<u8>>,
}
impl VcpValue {
pub fn new(value: u8) -> Self {
VcpValue {
value,
sub_values: None,
}
}
pub fn sub_values(&self) -> &[u8] {
self.sub_values.as_ref().map(|v| &v[..]).unwrap_or_default()
}
}
impl fmt::Debug for VcpValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut debug = f.debug_tuple("VcpValue");
let debug = debug.field(&self.value);
match self.sub_values() {
&[] => debug.finish(),
values => debug.field(&values).finish(),
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Vcp {
pub feature: u8,
pub values: Option<Vec<VcpValue>>,
}
impl Vcp {
pub fn values(&self) -> &[VcpValue] {
self.values.as_ref().map(|v| &v[..]).unwrap_or_default()
}
}
impl fmt::Debug for Vcp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut debug = f.debug_tuple("Vcp");
let debug = debug.field(&self.feature);
match self.values() {
&[] => debug.finish(),
values => debug.field(&values).finish(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VcpName<'i> {
pub feature: u8,
pub name: Option<Cow<'i, str>>,
pub value_names: Option<Vec<Cow<'i, str>>>,
}
impl<'i> VcpName<'i> {
pub fn value_names(&self) -> &[Cow<'i, str>] {
self.value_names.as_ref().map(|v| &v[..]).unwrap_or_default()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Cap<'a> {
Protocol(&'a str),
Type(&'a str),
Model(&'a str),
Commands(Vec<u8>),
Whql(u8),
MccsVersion(u8, u8),
Vcp(Vec<Vcp>),
VcpNames(Vec<VcpName<'a>>),
Edid(&'a [u8]),
Vdif(&'a [u8]),
Unknown(Value<'a>),
}
impl<'i> Cap<'i> {
pub fn parse_entries(entries: ValueParser<'i>) -> impl Iterator<Item = io::Result<Cap<'i>>> + 'i {
entries
.nom_iter()
.map(|e| e.and_then(|e| Self::parse_entry(e)).map_err(map_err))
}
pub fn parse_entry(value: Value<'i>) -> OResult<'i, Cap<'i>> {
match value {
Value::String { tag, value } => Self::parse_string(tag, value),
Value::Binary { tag, data } => Ok(Self::parse_data(tag, data)),
}
}
pub fn parse_data(tag: &'i str, i: &'i [u8]) -> Cap<'i> {
match tag {
"edid" => Cap::Edid(i),
"vdif" => Cap::Vdif(i),
_ => Cap::Unknown(Value::Binary { tag, data: i }),
}
}
pub fn parse_string(tag: &'i str, i: &'i [u8]) -> OResult<'i, Cap<'i>> {
match tag {
"prot" => all_consuming(map(value, Cap::Protocol))(i),
"type" => all_consuming(map(value, Cap::Type))(i),
"model" => all_consuming(map(value, Cap::Model))(i),
"cmds" => all_consuming(map(hexarray, Cap::Commands))(i),
"mswhql" => all_consuming(map(map_parser(take(1usize), u8), Cap::Whql))(i),
"mccs_ver" => all_consuming(map(mccs_ver, |(major, minor)| Cap::MccsVersion(major, minor)))(i),
"vcp" | "VCP" => all_consuming(map(trim_spaces(many0(vcp)), Cap::Vcp))(i),
"vcpname" => all_consuming(map(many0(vcpname), Cap::VcpNames))(i),
_ => Ok((Default::default(), Cap::Unknown(Value::String { tag, value: i }))),
}
.finish()
.map(|(_, c)| c)
}
}
fn backslash_escape(i: &[u8]) -> IResult<&[u8], String> {
let escaped = |i| {
let (i, _) = tag("\\x")(i)?;
map_str(take(2usize), |h| u8::from_str_radix(h, 16).map(|v| v as char), i)
};
fold_many0(
alt((
escaped,
map(take(1usize), |s: &[u8]| s[0] as char), )),
|| String::new(),
|mut s: String, c| {
s.push(c);
s
},
)(i)
}
fn value_escape_nospace(i: &[u8]) -> IResult<&[u8], Cow<str>> {
map_parser(
is_not(" ()"),
alt((
all_consuming(map(map_res(is_not("\\"), str::from_utf8), Cow::Borrowed)),
map(all_consuming(backslash_escape), Cow::Owned),
)),
)(i)
}
fn value(i: &[u8]) -> IResult<&[u8], &str> {
map_res(rest, str::from_utf8)(i)
}
fn hexarray(i: &[u8]) -> IResult<&[u8], Vec<u8>> {
many0(trim_spaces(hexvalue))(i)
}
fn map_str<'i, O, E2, F, G>(mut parser: F, f: G, i: &'i [u8]) -> IResult<&'i [u8], O>
where
F: nom::Parser<&'i [u8], &'i [u8], nom::error::Error<&'i [u8]>>,
G: FnMut(&'i str) -> Result<O, E2>,
{
use nom::Parser;
let mut f = map_res(rest, f);
let (i, s) = map_res(|i| parser.parse(i), |i| str::from_utf8(i.into()))(i)?;
match f.parse(s) {
Ok((_, v)) => Ok((i, v)),
Err(e) => Err(e.map(|e: nom::error::Error<_>| nom::error::Error { input: i, code: e.code })),
}
}
fn hexvalue(i: &[u8]) -> IResult<&[u8], u8> {
map_str(take(2usize), |s| u8::from_str_radix(s, 16), i)
}
fn vcp_value(i: &[u8]) -> IResult<&[u8], VcpValue> {
map(
tuple((trim_spaces(hexvalue), opt(bracketed(many0(trim_spaces(hexvalue)))))),
|(value, sub_values)| VcpValue { value, sub_values },
)(i)
}
fn vcp(i: &[u8]) -> IResult<&[u8], Vcp> {
let featurevalues = bracketed(many0(trim_spaces(vcp_value)));
map(
tuple((trim_spaces(hexvalue), opt(featurevalues))),
|(feature, values)| Vcp { feature, values },
)(i)
}
fn vcpname(i: &[u8]) -> IResult<&[u8], VcpName> {
let (i, feature) = trim_spaces(hexvalue)(i)?;
let (i, (name, value_names)) = bracketed(tuple((
opt(value_escape_nospace),
opt(bracketed(trim_spaces(separated_list0(space1, value_escape_nospace)))),
)))(i)?;
Ok((i, VcpName {
feature,
name,
value_names,
}))
}
fn mccs_ver(i: &[u8]) -> IResult<&[u8], (u8, u8)> {
alt((
separated_pair(u8, char('.'), u8),
tuple((map_parser(take(2usize), u8), map_parser(take(2usize), u8))),
))(i)
}
#[test]
fn vcpname_temp() {
let testdata = br"14((9300 6500 5500))44(Rotate)80(Do\x20this(On Off))82(Fixit)";
let expected = [
VcpName {
feature: 0x14,
name: None,
value_names: Some(vec!["9300".into(), "6500".into(), "5500".into()]),
},
VcpName {
feature: 0x44,
name: Some("Rotate".into()),
value_names: None,
},
VcpName {
feature: 0x80,
name: Some("Do this".into()),
value_names: Some(vec!["On".into(), "Off".into()]),
},
VcpName {
feature: 0x82,
name: Some("Fixit".into()),
value_names: None,
},
];
let (_, vcps) = all_consuming(many0(vcpname))(testdata).finish().unwrap();
assert_eq!(vcps.len(), expected.len());
for (vcp, exp) in vcps.into_iter().zip(expected) {
assert_eq!(vcp, exp);
}
}
#[test]
fn vcpname_brightness() {
let testdata = b"10(Brightness)";
let expected = VcpName {
feature: 0x10,
name: Some("Brightness".into()),
value_names: None,
};
let (_, vcp) = all_consuming(vcpname)(testdata).finish().unwrap();
assert_eq!(vcp, expected);
}