1#![deny(missing_docs)]
2#![doc(html_root_url = "https://docs.rs/mccs-caps/0.2.0")]
3
4pub use self::{
10 caps::{Cap, Vcp, VcpName, VcpValue},
11 entries::ValueParser,
12};
13use {
14 mccs::{Capabilities, UnknownData, UnknownTag, VcpDescriptor, Version},
15 nom::Finish,
16 std::{fmt, io, str},
17};
18
19#[cfg(test)]
20mod testdata;
21
22#[allow(missing_docs)]
23mod caps;
24#[allow(missing_docs)]
25mod entries;
26
27pub fn parse_capabilities<C: AsRef<[u8]>>(capability_string: C) -> io::Result<Capabilities> {
29 let capability_string = capability_string.as_ref();
30 let entries = Value::parse_capabilities(capability_string);
31
32 let mut caps = Capabilities::default();
35 let mut vcpnames = Vec::new();
36 for cap in Cap::parse_entries(entries) {
37 match cap? {
38 Cap::Protocol(protocol) => caps.protocol = Some(protocol.into()),
39 Cap::Type(ty) => caps.ty = Some(ty.into()),
40 Cap::Model(model) => caps.model = Some(model.into()),
41 Cap::Commands(ref cmds) => caps.commands = cmds.clone(),
42 Cap::Whql(whql) => caps.ms_whql = Some(whql),
43 Cap::MccsVersion(major, minor) => caps.mccs_version = Some(Version::new(major, minor)),
44 Cap::Vcp(ref vcp) =>
45 for Vcp {
46 feature: code,
47 ref values,
48 } in vcp
49 {
50 caps.vcp_features
51 .entry(*code)
52 .or_insert_with(|| VcpDescriptor::default())
53 .values
54 .extend(values.iter().flat_map(|i| i).map(|v| (v.value, None)))
55 },
56 Cap::VcpNames(v) => vcpnames.extend(v), Cap::Unknown(value) => caps.unknown_tags.push(UnknownTag {
58 name: value.tag().into(),
59 data: match value {
60 Value::String { value, .. } => match str::from_utf8(value) {
61 Ok(value) => UnknownData::String(value.into()),
62 Err(..) => UnknownData::StringBytes(value.into()),
63 },
64 Value::Binary { data, .. } => UnknownData::Binary(data.into()),
65 },
66 }),
67 Cap::Edid(edid) => caps.edid = Some(edid.into()),
68 Cap::Vdif(vdif) => caps.vdif.push(vdif.into()),
69 }
70 }
71
72 for VcpName {
73 feature: code,
74 name,
75 value_names,
76 } in vcpnames
77 {
78 if let Some(vcp) = caps.vcp_features.get_mut(&code) {
79 if let Some(name) = name {
80 vcp.name = Some(name.into())
81 }
82
83 if let Some(value_names) = value_names {
84 for ((_, dest), name) in vcp.values.iter_mut().zip(value_names) {
85 *dest = Some(name.into())
86 }
87 }
88 } else {
89 }
91 }
92
93 Ok(caps)
94}
95
96#[derive(Copy, Clone, Debug, PartialEq, Eq)]
98pub enum Value<'i> {
99 String {
101 tag: &'i str,
103 value: &'i [u8],
105 },
106 Binary {
108 tag: &'i str,
110 data: &'i [u8],
112 },
113}
114
115impl<'i> Value<'i> {
116 pub fn parse_capabilities(capability_string: &'i [u8]) -> ValueParser<'i> {
118 ValueParser::new(capability_string)
119 }
120
121 pub fn parse(data: &'i str) -> io::Result<Self> {
123 Self::parse_bytes(data.as_bytes())
124 }
125
126 pub fn parse_bytes(data: &'i [u8]) -> io::Result<Self> {
128 Self::parse_nom(data, None).finish().map(|(_, v)| v).map_err(map_err)
129 }
130
131 pub fn tag(&self) -> &'i str {
133 match *self {
134 Value::String { tag, .. } => tag,
135 Value::Binary { tag, .. } => tag,
136 }
137 }
138}
139
140impl From<Value<'_>> for UnknownTag {
141 fn from(v: Value) -> Self {
142 UnknownTag {
143 name: v.tag().into(),
144 data: match v {
145 Value::Binary { data, .. } => UnknownData::Binary(data.into()),
146 Value::String { value, .. } => match str::from_utf8(value) {
147 Ok(value) => UnknownData::String(value.into()),
148 Err(_) => UnknownData::StringBytes(value.into()),
149 },
150 },
151 }
152 }
153}
154
155impl<'a> From<&'a UnknownTag> for Value<'a> {
156 fn from(v: &'a UnknownTag) -> Self {
157 let tag = &v.name;
158 match &v.data {
159 UnknownData::Binary(data) => Value::Binary { tag, data },
160 UnknownData::StringBytes(value) => Value::String { tag, value },
161 UnknownData::String(value) => Value::String {
162 tag,
163 value: value.as_bytes(),
164 },
165 }
166 }
167}
168
169impl<'i> fmt::Display for Value<'i> {
170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171 match self {
172 Value::String { tag, value } => write!(f, "{tag}({})", value.escape_ascii()),
173 Value::Binary { tag, data } => write!(f, "{tag} bin({}({}))", data.len(), data.escape_ascii()),
174 }
175 }
176}
177
178pub(crate) type OResult<'i, O> = Result<O, nom::error::Error<&'i [u8]>>;
179pub(crate) type OResultI<'i, O> = Result<O, nom::Err<nom::error::Error<&'i [u8]>>>;
180
181pub(crate) fn map_err(e: nom::error::Error<&[u8]>) -> io::Error {
182 use nom::error::{Error, ErrorKind};
183
184 io::Error::new(
185 match e.code {
186 ErrorKind::Eof | ErrorKind::Complete => io::ErrorKind::UnexpectedEof,
187 _ => io::ErrorKind::InvalidData,
188 },
189 Error {
190 input: e.input.escape_ascii().to_string(),
191 code: e.code,
192 },
193 )
194}
195
196pub(crate) fn trim_spaces<I, O, E, P>(parser: P) -> impl FnMut(I) -> nom::IResult<I, O, E>
197where
198 P: nom::Parser<I, O, E>,
199 E: nom::error::ParseError<I>,
200 I: Clone + nom::InputTakeAtPosition,
201 <I as nom::InputTakeAtPosition>::Item: nom::AsChar + Clone,
202{
203 use nom::{character::complete::space0, sequence::delimited};
204
205 delimited(space0, parser, space0)
206}
207
208pub(crate) fn bracketed<I, O, E, P>(parser: P) -> impl FnMut(I) -> nom::IResult<I, O, E>
209where
210 P: nom::Parser<I, O, E>,
211 E: nom::error::ParseError<I>,
212 I: Clone + nom::Slice<std::ops::RangeFrom<usize>> + nom::InputIter,
213 <I as nom::InputIter>::Item: nom::AsChar,
214{
215 use nom::{character::complete::char, sequence::delimited};
216
217 delimited(char('('), parser, char(')'))
218}
219
220#[test]
221fn samples_entries() {
222 for sample in testdata::test_data() {
223 println!("Parsing caps: {}", String::from_utf8_lossy(sample));
224 for cap in Value::parse_capabilities(sample).nom_iter() {
225 println!("entry: {:?}", cap.unwrap());
226 }
227 }
228}
229
230#[test]
231fn samples_caps() {
232 for sample in testdata::test_data() {
233 println!("Parsing caps: {}", String::from_utf8_lossy(sample));
234 let ent = Value::parse_capabilities(sample);
235 for (cap, end) in Cap::parse_entries(ent.clone()).zip(ent) {
236 println!("{}", end.unwrap());
237 println!("{:?}", cap.unwrap());
238 }
239 }
240}
241
242#[test]
243fn samples_high() {
244 for sample in testdata::test_data() {
245 let caps = parse_capabilities(sample).expect("Failed to parse capabilities");
246 println!("Caps: {:#?}", caps);
247 }
248}