1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use super::{CtrlFrame, CtrlSubtype};
use zerocopy::{Ref, SplitByteSlice};

mod fields;

pub use fields::*;

#[derive(Debug)]
pub enum CtrlBody<B: SplitByteSlice> {
    PsPoll { ps_poll: Ref<B, PsPoll> },
    Unsupported { subtype: CtrlSubtype },
}

impl<B: SplitByteSlice> CtrlBody<B> {
    pub fn parse(subtype: CtrlSubtype, bytes: B) -> Option<Self> {
        match subtype {
            CtrlSubtype::PS_POLL => {
                let (ps_poll, _) = Ref::from_prefix(bytes).ok()?;
                Some(CtrlBody::PsPoll { ps_poll })
            }
            subtype => Some(CtrlBody::Unsupported { subtype }),
        }
    }
}

impl<B> TryFrom<CtrlFrame<B>> for CtrlBody<B>
where
    B: SplitByteSlice,
{
    type Error = ();

    fn try_from(ctrl_frame: CtrlFrame<B>) -> Result<Self, Self::Error> {
        CtrlBody::parse(ctrl_frame.ctrl_subtype(), ctrl_frame.body).ok_or(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assert_variant;
    use ieee80211::{Bssid, MacAddr};

    #[test]
    fn parse_ps_poll_frame() {
        let bytes = vec![
            0b00000001, 0b11000000, // Masked AID
            2, 2, 2, 2, 2, 2, // addr1
            4, 4, 4, 4, 4, 4, // addr2
        ];
        assert_variant!(
            CtrlBody::parse(CtrlSubtype::PS_POLL, &bytes[..]),
            Some(CtrlBody::PsPoll { ps_poll }) => {
                assert_eq!(0b1100000000000001, { ps_poll.masked_aid });
                assert_eq!(Bssid::from([2; 6]), ps_poll.bssid);
                assert_eq!(MacAddr::from([4; 6]), ps_poll.ta);
            },
            "expected PS-Poll frame"
        );
    }
}