libmonitor/mccs/capabilities/
mod.rs1mod entries;
5mod parsers;
6
7use crate::ddc::ci::DdcOpcode;
8
9use self::{entries::ValueParser, parsers::Cap};
10use super::{features::VcpCapability, DisplayTechnology, Protocol};
11
12use {
13 crate::mccs::{UnknownData, UnknownTag, Version},
14 nom::Finish,
15 std::{fmt, io, str},
16};
17
18#[derive(Debug, Default, Clone)]
20pub struct Capabilities {
21 pub protocol: Option<Protocol>,
23 pub ty: Option<DisplayTechnology>,
25 pub model: Option<String>,
27 pub commands: Vec<DdcOpcode>,
29 pub ms_whql: Option<u8>,
32 pub mccs_version: Option<Version>,
34 pub vcp_features: Vec<VcpCapability>,
36 pub unknown_tags: Vec<UnknownTag>,
38}
39
40pub fn parse_capabilities<C: AsRef<[u8]>>(capability_string: C) -> io::Result<Capabilities> {
42 let capability_string = capability_string.as_ref();
43 let entries = Value::parse_capabilities(capability_string);
44
45 let mut caps = Capabilities::default();
46 for cap in Cap::parse_entries(entries) {
47 match cap? {
48 Cap::Protocol(protocol) => caps.protocol = Some(protocol.into()),
49 Cap::Type(ty) => caps.ty = Some(ty.into()),
50 Cap::Model(model) => caps.model = Some(model.into()),
51 Cap::Commands(ref cmds) => caps.commands = cmds.clone(),
52 Cap::Whql(whql) => caps.ms_whql = Some(whql),
53 Cap::MccsVersion(major, minor) => caps.mccs_version = Some(Version::new(major, minor)),
54 Cap::Vcp(vcp) => {
55 for v in vcp {
56 caps.vcp_features.push(v);
57 }
58 }
59 Cap::Unknown(value) => caps.unknown_tags.push(UnknownTag {
60 name: value.tag().into(),
61 data: match value {
62 Value::String { value, .. } => match str::from_utf8(value) {
63 Ok(value) => UnknownData::String(value.into()),
64 Err(..) => UnknownData::StringBytes(value.into()),
65 },
66 Value::Binary { data, .. } => UnknownData::Binary(data.into()),
67 },
68 }),
69 }
70 }
71
72 Ok(caps)
73}
74
75#[derive(Copy, Clone, Debug, PartialEq, Eq)]
77pub enum Value<'i> {
78 String {
80 tag: &'i str,
82 value: &'i [u8],
84 },
85 Binary {
87 tag: &'i str,
89 data: &'i [u8],
91 },
92}
93
94impl<'i> Value<'i> {
95 pub fn parse_capabilities(capability_string: &'i [u8]) -> ValueParser<'i> {
97 ValueParser::new(capability_string)
98 }
99
100 pub fn parse(data: &'i str) -> io::Result<Self> {
102 Self::parse_bytes(data.as_bytes())
103 }
104
105 pub fn parse_bytes(data: &'i [u8]) -> io::Result<Self> {
107 Self::parse_nom(data, None)
108 .finish()
109 .map(|(_, v)| v)
110 .map_err(map_err)
111 }
112
113 pub fn tag(&self) -> &'i str {
115 match *self {
116 Value::String { tag, .. } => tag,
117 Value::Binary { tag, .. } => tag,
118 }
119 }
120}
121
122impl From<Value<'_>> for UnknownTag {
123 fn from(v: Value) -> Self {
124 UnknownTag {
125 name: v.tag().into(),
126 data: match v {
127 Value::Binary { data, .. } => UnknownData::Binary(data.into()),
128 Value::String { value, .. } => match str::from_utf8(value) {
129 Ok(value) => UnknownData::String(value.into()),
130 Err(_) => UnknownData::StringBytes(value.into()),
131 },
132 },
133 }
134 }
135}
136
137impl<'a> From<&'a UnknownTag> for Value<'a> {
138 fn from(v: &'a UnknownTag) -> Self {
139 let tag = &v.name;
140 match &v.data {
141 UnknownData::Binary(data) => Value::Binary { tag, data },
142 UnknownData::StringBytes(value) => Value::String { tag, value },
143 UnknownData::String(value) => Value::String {
144 tag,
145 value: value.as_bytes(),
146 },
147 }
148 }
149}
150
151impl<'i> fmt::Display for Value<'i> {
152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 match self {
154 Value::String { tag, value } => write!(f, "{tag}({})", value.escape_ascii()),
155 Value::Binary { tag, data } => {
156 write!(f, "{tag} bin({}({}))", data.len(), data.escape_ascii())
157 }
158 }
159 }
160}
161
162pub(crate) type OResult<'i, O> = Result<O, nom::error::Error<&'i [u8]>>;
163pub(crate) type OResultI<'i, O> = Result<O, nom::Err<nom::error::Error<&'i [u8]>>>;
164
165pub(crate) fn map_err(e: nom::error::Error<&[u8]>) -> io::Error {
166 use nom::error::{Error, ErrorKind};
167
168 io::Error::new(
169 match e.code {
170 ErrorKind::Eof | ErrorKind::Complete => io::ErrorKind::UnexpectedEof,
171 _ => io::ErrorKind::InvalidData,
172 },
173 Error {
174 input: e.input.escape_ascii().to_string(),
175 code: e.code,
176 },
177 )
178}
179
180pub(crate) fn trim_spaces<I, O, E, P>(parser: P) -> impl FnMut(I) -> nom::IResult<I, O, E>
181where
182 P: nom::Parser<I, O, E>,
183 E: nom::error::ParseError<I>,
184 I: Clone + nom::InputTakeAtPosition,
185 <I as nom::InputTakeAtPosition>::Item: nom::AsChar + Clone,
186{
187 use nom::{character::complete::space0, sequence::delimited};
188
189 delimited(space0, parser, space0)
190}
191
192pub(crate) fn bracketed<I, O, E, P>(parser: P) -> impl FnMut(I) -> nom::IResult<I, O, E>
193where
194 P: nom::Parser<I, O, E>,
195 E: nom::error::ParseError<I>,
196 I: Clone + nom::Slice<std::ops::RangeFrom<usize>> + nom::InputIter,
197 <I as nom::InputIter>::Item: nom::AsChar,
198{
199 use nom::{character::complete::char, sequence::delimited};
200
201 delimited(char('('), parser, char(')'))
202}