aboutsummaryrefslogtreecommitdiffstats
path: root/src/frame.rs
blob: 17c6c5aff7e0b5303321577472066e0050a005f8 (plain) (blame)
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
65
66
67
68
69
70
71
72
73
74
/// Represents a single ttyrec frame.
///
/// Ttyrec files are a raw concatenation of frames. Note that the `time` field
/// in each frame is the time since the start of the entire file, and it is
/// invalid for the `time` fields in a ttyrec file to be decreasing.
///
/// Frame objects are typically created via the `Creator`, `Parser`, or
/// `Reader` classes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Frame {
    /// Amount of time passed since the start of the ttyrec file.
    ///
    /// Note that this is *not* the amount of time since the previous frame.
    pub time: std::time::Duration,

    /// Bytes emitted at the given time.
    pub data: Vec<u8>,
}

impl std::convert::TryFrom<Frame> for Vec<u8> {
    type Error = crate::error::Error;

    fn try_from(frame: Frame) -> crate::error::Result<Self> {
        let secs = u32::try_from(frame.time.as_secs()).map_err(|_| {
            crate::error::Error::FrameTooLong {
                input: frame.time.as_secs(),
            }
        })?;
        let micros = frame.time.subsec_micros();
        let len = u32::try_from(frame.data.len()).map_err(|_| {
            crate::error::Error::FrameTooBig {
                input: frame.data.len(),
            }
        })?;
        let mut bytes = vec![];
        bytes.extend(secs.to_le_bytes().iter());
        bytes.extend(micros.to_le_bytes().iter());
        bytes.extend(len.to_le_bytes().iter());
        bytes.extend(frame.data.iter());
        Ok(bytes)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::convert::TryFrom as _;

    #[test]
    fn test_basic() {
        let tests = vec![
            (
                Frame {
                    time: std::time::Duration::new(0, 0),
                    data: vec![],
                },
                vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            ),
            (
                Frame {
                    time: std::time::Duration::new(38, 123_456_000),
                    data: b"\x1b[2Jfoobar".to_vec(),
                },
                vec![
                    38, 0, 0, 0, 64, 226, 1, 0, 10, 0, 0, 0, 27, 91, 50, 74,
                    102, 111, 111, 98, 97, 114,
                ],
            ),
        ];
        for (frame, bytes) in tests {
            assert_eq!(Vec::<u8>::try_from(frame).unwrap(), bytes);
        }
    }
}