#![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/mccs-caps/0.2.0")]
pub use self::{
caps::{Cap, Vcp, VcpName, VcpValue},
entries::ValueParser,
};
use {
mccs::{Capabilities, UnknownData, UnknownTag, VcpDescriptor, Version},
nom::Finish,
std::{fmt, io, str},
};
#[cfg(test)]
mod testdata;
#[allow(missing_docs)]
mod caps;
#[allow(missing_docs)]
mod entries;
pub fn parse_capabilities<C: AsRef<[u8]>>(capability_string: C) -> io::Result<Capabilities> {
let capability_string = capability_string.as_ref();
let entries = Value::parse_capabilities(capability_string);
let mut caps = Capabilities::default();
let mut vcpnames = Vec::new();
for cap in Cap::parse_entries(entries) {
match cap? {
Cap::Protocol(protocol) => caps.protocol = Some(protocol.into()),
Cap::Type(ty) => caps.ty = Some(ty.into()),
Cap::Model(model) => caps.model = Some(model.into()),
Cap::Commands(ref cmds) => caps.commands = cmds.clone(),
Cap::Whql(whql) => caps.ms_whql = Some(whql),
Cap::MccsVersion(major, minor) => caps.mccs_version = Some(Version::new(major, minor)),
Cap::Vcp(ref vcp) =>
for Vcp {
feature: code,
ref values,
} in vcp
{
caps.vcp_features
.entry(*code)
.or_insert_with(|| VcpDescriptor::default())
.values
.extend(values.iter().flat_map(|i| i).map(|v| (v.value, None)))
},
Cap::VcpNames(v) => vcpnames.extend(v), Cap::Unknown(value) => caps.unknown_tags.push(UnknownTag {
name: value.tag().into(),
data: match value {
Value::String { value, .. } => match str::from_utf8(value) {
Ok(value) => UnknownData::String(value.into()),
Err(..) => UnknownData::StringBytes(value.into()),
},
Value::Binary { data, .. } => UnknownData::Binary(data.into()),
},
}),
Cap::Edid(edid) => caps.edid = Some(edid.into()),
Cap::Vdif(vdif) => caps.vdif.push(vdif.into()),
}
}
for VcpName {
feature: code,
name,
value_names,
} in vcpnames
{
if let Some(vcp) = caps.vcp_features.get_mut(&code) {
if let Some(name) = name {
vcp.name = Some(name.into())
}
if let Some(value_names) = value_names {
for ((_, dest), name) in vcp.values.iter_mut().zip(value_names) {
*dest = Some(name.into())
}
}
} else {
}
}
Ok(caps)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Value<'i> {
String {
tag: &'i str,
value: &'i [u8],
},
Binary {
tag: &'i str,
data: &'i [u8],
},
}
impl<'i> Value<'i> {
pub fn parse_capabilities(capability_string: &'i [u8]) -> ValueParser<'i> {
ValueParser::new(capability_string)
}
pub fn parse(data: &'i str) -> io::Result<Self> {
Self::parse_bytes(data.as_bytes())
}
pub fn parse_bytes(data: &'i [u8]) -> io::Result<Self> {
Self::parse_nom(data, None).finish().map(|(_, v)| v).map_err(map_err)
}
pub fn tag(&self) -> &'i str {
match *self {
Value::String { tag, .. } => tag,
Value::Binary { tag, .. } => tag,
}
}
}
impl From<Value<'_>> for UnknownTag {
fn from(v: Value) -> Self {
UnknownTag {
name: v.tag().into(),
data: match v {
Value::Binary { data, .. } => UnknownData::Binary(data.into()),
Value::String { value, .. } => match str::from_utf8(value) {
Ok(value) => UnknownData::String(value.into()),
Err(_) => UnknownData::StringBytes(value.into()),
},
},
}
}
}
impl<'a> From<&'a UnknownTag> for Value<'a> {
fn from(v: &'a UnknownTag) -> Self {
let tag = &v.name;
match &v.data {
UnknownData::Binary(data) => Value::Binary { tag, data },
UnknownData::StringBytes(value) => Value::String { tag, value },
UnknownData::String(value) => Value::String {
tag,
value: value.as_bytes(),
},
}
}
}
impl<'i> fmt::Display for Value<'i> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Value::String { tag, value } => write!(f, "{tag}({})", value.escape_ascii()),
Value::Binary { tag, data } => write!(f, "{tag} bin({}({}))", data.len(), data.escape_ascii()),
}
}
}
pub(crate) type OResult<'i, O> = Result<O, nom::error::Error<&'i [u8]>>;
pub(crate) type OResultI<'i, O> = Result<O, nom::Err<nom::error::Error<&'i [u8]>>>;
pub(crate) fn map_err(e: nom::error::Error<&[u8]>) -> io::Error {
use nom::error::{Error, ErrorKind};
io::Error::new(
match e.code {
ErrorKind::Eof | ErrorKind::Complete => io::ErrorKind::UnexpectedEof,
_ => io::ErrorKind::InvalidData,
},
Error {
input: e.input.escape_ascii().to_string(),
code: e.code,
},
)
}
pub(crate) fn trim_spaces<I, O, E, P>(parser: P) -> impl FnMut(I) -> nom::IResult<I, O, E>
where
P: nom::Parser<I, O, E>,
E: nom::error::ParseError<I>,
I: Clone + nom::InputTakeAtPosition,
<I as nom::InputTakeAtPosition>::Item: nom::AsChar + Clone,
{
use nom::{character::complete::space0, sequence::delimited};
delimited(space0, parser, space0)
}
pub(crate) fn bracketed<I, O, E, P>(parser: P) -> impl FnMut(I) -> nom::IResult<I, O, E>
where
P: nom::Parser<I, O, E>,
E: nom::error::ParseError<I>,
I: Clone + nom::Slice<std::ops::RangeFrom<usize>> + nom::InputIter,
<I as nom::InputIter>::Item: nom::AsChar,
{
use nom::{character::complete::char, sequence::delimited};
delimited(char('('), parser, char(')'))
}
#[test]
fn samples_entries() {
for sample in testdata::test_data() {
println!("Parsing caps: {}", String::from_utf8_lossy(sample));
for cap in Value::parse_capabilities(sample).nom_iter() {
println!("entry: {:?}", cap.unwrap());
}
}
}
#[test]
fn samples_caps() {
for sample in testdata::test_data() {
println!("Parsing caps: {}", String::from_utf8_lossy(sample));
let ent = Value::parse_capabilities(sample);
for (cap, end) in Cap::parse_entries(ent.clone()).zip(ent) {
println!("{}", end.unwrap());
println!("{:?}", cap.unwrap());
}
}
}
#[test]
fn samples_high() {
for sample in testdata::test_data() {
let caps = parse_capabilities(sample).expect("Failed to parse capabilities");
println!("Caps: {:#?}", caps);
}
}