mccs_caps/
caps.rs

1use {
2    super::{bracketed, map_err, trim_spaces, OResult, Value, ValueParser},
3    nom::{
4        branch::alt,
5        bytes::complete::{is_not, tag, take},
6        character::complete::{char, space1, u8},
7        combinator::{all_consuming, map, map_parser, map_res, opt, rest},
8        multi::{fold_many0, many0, separated_list0},
9        sequence::{separated_pair, tuple},
10        Finish, IResult,
11    },
12    std::{borrow::Cow, fmt, io, str},
13};
14
15#[derive(Clone, PartialEq, Eq)]
16pub struct VcpValue {
17    pub value: u8,
18    pub sub_values: Option<Vec<u8>>,
19}
20
21impl VcpValue {
22    pub fn new(value: u8) -> Self {
23        VcpValue {
24            value,
25            sub_values: None,
26        }
27    }
28
29    pub fn sub_values(&self) -> &[u8] {
30        self.sub_values.as_ref().map(|v| &v[..]).unwrap_or_default()
31    }
32}
33
34impl fmt::Debug for VcpValue {
35    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36        let mut debug = f.debug_tuple("VcpValue");
37        let debug = debug.field(&self.value);
38        match self.sub_values() {
39            &[] => debug.finish(),
40            values => debug.field(&values).finish(),
41        }
42    }
43}
44
45#[derive(Clone, PartialEq, Eq)]
46pub struct Vcp {
47    pub feature: u8,
48    pub values: Option<Vec<VcpValue>>,
49}
50
51impl Vcp {
52    pub fn values(&self) -> &[VcpValue] {
53        self.values.as_ref().map(|v| &v[..]).unwrap_or_default()
54    }
55}
56
57impl fmt::Debug for Vcp {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        let mut debug = f.debug_tuple("Vcp");
60        let debug = debug.field(&self.feature);
61        match self.values() {
62            &[] => debug.finish(),
63            values => debug.field(&values).finish(),
64        }
65    }
66}
67
68#[derive(Clone, Debug, PartialEq, Eq)]
69pub struct VcpName<'i> {
70    pub feature: u8,
71    pub name: Option<Cow<'i, str>>,
72    pub value_names: Option<Vec<Cow<'i, str>>>,
73}
74
75impl<'i> VcpName<'i> {
76    pub fn value_names(&self) -> &[Cow<'i, str>] {
77        self.value_names.as_ref().map(|v| &v[..]).unwrap_or_default()
78    }
79}
80
81/// Parsed display capabilities string entry
82#[derive(Clone, Debug, PartialEq, Eq)]
83pub enum Cap<'a> {
84    Protocol(&'a str),
85    Type(&'a str),
86    Model(&'a str),
87    Commands(Vec<u8>),
88    Whql(u8),
89    MccsVersion(u8, u8),
90    Vcp(Vec<Vcp>),
91    VcpNames(Vec<VcpName<'a>>),
92    Edid(&'a [u8]),
93    Vdif(&'a [u8]),
94    Unknown(Value<'a>),
95}
96
97impl<'i> Cap<'i> {
98    pub fn parse_entries(entries: ValueParser<'i>) -> impl Iterator<Item = io::Result<Cap<'i>>> + 'i {
99        entries
100            .nom_iter()
101            .map(|e| e.and_then(|e| Self::parse_entry(e)).map_err(map_err))
102    }
103
104    pub fn parse_entry(value: Value<'i>) -> OResult<'i, Cap<'i>> {
105        match value {
106            Value::String { tag, value } => Self::parse_string(tag, value),
107            Value::Binary { tag, data } => Ok(Self::parse_data(tag, data)),
108        }
109    }
110
111    pub fn parse_data(tag: &'i str, i: &'i [u8]) -> Cap<'i> {
112        match tag {
113            "edid" => Cap::Edid(i),
114            "vdif" => Cap::Vdif(i),
115            _ => Cap::Unknown(Value::Binary { tag, data: i }),
116        }
117    }
118
119    pub fn parse_string(tag: &'i str, i: &'i [u8]) -> OResult<'i, Cap<'i>> {
120        match tag {
121            "prot" => all_consuming(map(value, Cap::Protocol))(i),
122            "type" => all_consuming(map(value, Cap::Type))(i),
123            "model" => all_consuming(map(value, Cap::Model))(i),
124            "cmds" => all_consuming(map(hexarray, Cap::Commands))(i),
125            "mswhql" => all_consuming(map(map_parser(take(1usize), u8), Cap::Whql))(i),
126            "mccs_ver" => all_consuming(map(mccs_ver, |(major, minor)| Cap::MccsVersion(major, minor)))(i),
127            // hack for Apple Cinema Display
128            "vcp" | "VCP" => all_consuming(map(trim_spaces(many0(vcp)), Cap::Vcp))(i),
129            "vcpname" => all_consuming(map(many0(vcpname), Cap::VcpNames))(i),
130            _ => Ok((Default::default(), Cap::Unknown(Value::String { tag, value: i }))),
131        }
132        .finish()
133        .map(|(_, c)| c)
134    }
135}
136
137fn backslash_escape(i: &[u8]) -> IResult<&[u8], String> {
138    // TODO: I'd use https://docs.rs/nom/7.1.1/nom/bytes/complete/fn.escaped_transform.html instead,
139    // but it can't deal with dynamic transforms due to ExtendInto not being impl'd on anything useful
140    // like Vec<u8> or [u8; N] or something...
141    let escaped = |i| {
142        let (i, _) = tag("\\x")(i)?;
143        map_str(take(2usize), |h| u8::from_str_radix(h, 16).map(|v| v as char), i)
144    };
145    fold_many0(
146        alt((
147            escaped,
148            // TODO: other escapes like \\ \n etc? unclear in access bus spec...
149            map(take(1usize), |s: &[u8]| s[0] as char), // TODO, this isn't utf8 parsing, should it be? .-.
150        )),
151        || String::new(),
152        |mut s: String, c| {
153            s.push(c);
154            s
155        },
156    )(i)
157}
158
159fn value_escape_nospace(i: &[u8]) -> IResult<&[u8], Cow<str>> {
160    map_parser(
161        is_not(" ()"),
162        alt((
163            all_consuming(map(map_res(is_not("\\"), str::from_utf8), Cow::Borrowed)),
164            map(all_consuming(backslash_escape), Cow::Owned),
165        )),
166    )(i)
167}
168
169fn value(i: &[u8]) -> IResult<&[u8], &str> {
170    map_res(rest, str::from_utf8)(i)
171}
172
173fn hexarray(i: &[u8]) -> IResult<&[u8], Vec<u8>> {
174    many0(trim_spaces(hexvalue))(i)
175}
176
177fn map_str<'i, O, E2, F, G>(mut parser: F, f: G, i: &'i [u8]) -> IResult<&'i [u8], O>
178where
179    F: nom::Parser<&'i [u8], &'i [u8], nom::error::Error<&'i [u8]>>,
180    G: FnMut(&'i str) -> Result<O, E2>,
181{
182    use nom::Parser;
183
184    let mut f = map_res(rest, f);
185    let (i, s) = map_res(|i| parser.parse(i), |i| str::from_utf8(i.into()))(i)?;
186    match f.parse(s) {
187        Ok((_, v)) => Ok((i, v)),
188        Err(e) => Err(e.map(|e: nom::error::Error<_>| nom::error::Error { input: i, code: e.code })),
189    }
190}
191
192fn hexvalue(i: &[u8]) -> IResult<&[u8], u8> {
193    map_str(take(2usize), |s| u8::from_str_radix(s, 16), i)
194}
195
196fn vcp_value(i: &[u8]) -> IResult<&[u8], VcpValue> {
197    map(
198        tuple((trim_spaces(hexvalue), opt(bracketed(many0(trim_spaces(hexvalue)))))),
199        |(value, sub_values)| VcpValue { value, sub_values },
200    )(i)
201}
202
203fn vcp(i: &[u8]) -> IResult<&[u8], Vcp> {
204    let featurevalues = bracketed(many0(trim_spaces(vcp_value)));
205    map(
206        tuple((trim_spaces(hexvalue), opt(featurevalues))),
207        |(feature, values)| Vcp { feature, values },
208    )(i)
209}
210
211fn vcpname(i: &[u8]) -> IResult<&[u8], VcpName> {
212    let (i, feature) = trim_spaces(hexvalue)(i)?;
213    let (i, (name, value_names)) = bracketed(tuple((
214        opt(value_escape_nospace),
215        opt(bracketed(trim_spaces(separated_list0(space1, value_escape_nospace)))),
216    )))(i)?;
217    Ok((i, VcpName {
218        feature,
219        name,
220        value_names,
221    }))
222}
223
224fn mccs_ver(i: &[u8]) -> IResult<&[u8], (u8, u8)> {
225    alt((
226        separated_pair(u8, char('.'), u8),
227        tuple((map_parser(take(2usize), u8), map_parser(take(2usize), u8))),
228    ))(i)
229}
230
231#[test]
232fn vcpname_temp() {
233    let testdata = br"14((9300 6500 5500))44(Rotate)80(Do\x20this(On Off))82(Fixit)";
234    let expected = [
235        VcpName {
236            feature: 0x14,
237            name: None,
238            value_names: Some(vec!["9300".into(), "6500".into(), "5500".into()]),
239        },
240        VcpName {
241            feature: 0x44,
242            name: Some("Rotate".into()),
243            value_names: None,
244        },
245        VcpName {
246            feature: 0x80,
247            name: Some("Do this".into()),
248            value_names: Some(vec!["On".into(), "Off".into()]),
249        },
250        VcpName {
251            feature: 0x82,
252            name: Some("Fixit".into()),
253            value_names: None,
254        },
255    ];
256
257    let (_, vcps) = all_consuming(many0(vcpname))(testdata).finish().unwrap();
258    assert_eq!(vcps.len(), expected.len());
259
260    for (vcp, exp) in vcps.into_iter().zip(expected) {
261        assert_eq!(vcp, exp);
262    }
263}
264
265#[test]
266fn vcpname_brightness() {
267    let testdata = b"10(Brightness)";
268    let expected = VcpName {
269        feature: 0x10,
270        name: Some("Brightness".into()),
271        value_names: None,
272    };
273    let (_, vcp) = all_consuming(vcpname)(testdata).finish().unwrap();
274
275    assert_eq!(vcp, expected);
276}