Skip to main content

subunit/
lib.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13#![deny(missing_docs)]
14
15//! Implementation of the Subunit protocol in Rust. For the protocol definition,
16//! see the [Subunit Protocol
17//! Specification](https://github.com/testing-cabal/subunit/blob/main/README.rst).
18//! This crate contains both a v1 and v2 implementation of the protocol. v1 is
19//! disabled by default but can be enabled via the `v1` feature.
20
21/// Types representing the Subunit protocol
22pub mod types {
23    pub mod event;
24    pub mod eventfeatures;
25    pub mod file;
26    pub mod number;
27    pub mod stream;
28    pub mod teststatus;
29    pub mod timestamp;
30}
31
32pub mod deserialize;
33pub mod io;
34pub mod serialize;
35
36/// Constants defined by the Subunit protocol
37pub mod constants {
38    /// The Subunit v2 protocol signature
39    pub static V2_SIGNATURE: u8 = 0xb3;
40
41    /// Maximum packet length
42    pub static MAX_PACKET_LENGTH: u32 = 4 * 1024 * 1024;
43
44    /// Maximum value for a number
45    pub static MAX_NUMBER_VALUE: u32 = 0x3fffffff;
46
47    /// Mask for a number kind
48    pub static NUMBER_KIND_MASK: u8 = 0xc0;
49
50    /// Mask for a number value
51    pub static NUMBER_VALUE_MASK: u8 = 0x3f;
52
53    /// The Subunit v2 protocol version
54    pub static VERSION2: u16 = 0x2000;
55}
56#[cfg(feature = "v1")]
57pub mod v1;
58
59use std::fmt::Debug;
60
61use thiserror::Error as ThisError;
62
63#[derive(ThisError)]
64// Allow missing docs, because #[error] will generate them
65#[allow(missing_docs)]
66/// Error type for parsing and serializing Subunit packets
67pub enum Error {
68    #[error("Value is too large to encode")]
69    TooLarge,
70    #[error("Invalid packet header: size {} < header size {}", _0, _1)]
71    LengthTooSmall(u32, u32),
72    #[error("Internal logic error {}", _0)]
73    Internal(String),
74    #[error("Invalid UTF8")]
75    InvalidUTF8Sequence,
76    #[error("Not enough bytes")]
77    NotEnoughBytes,
78    #[error("Invalid signature")]
79    InvalidSignature,
80    #[error("Bad version {:#x}", _0)]
81    BadVersion(u16),
82    #[error("CRC32 Mismatch measured: {:#02x} != stored: {:#02x}", _0, _1)]
83    CRC32Mismatch(u32, u32),
84    #[error("Invalid timestamp secs: {} nsecs: {}", _0, _1)]
85    InvalidTimestamp(u32, u32),
86    #[error("IO Error: {}", _0)]
87    IO(#[from] std::io::Error),
88    #[cfg(feature = "v1")]
89    #[error("V1 Parsing error: {:?}", _0)]
90    V1Parse(String),
91}
92
93#[cfg(feature = "v1")]
94impl<I, O> From<winnow::error::ParseError<I, O>> for Error
95where
96    I: std::fmt::Debug,
97    O: std::fmt::Debug,
98{
99    fn from(err: winnow::error::ParseError<I, O>) -> Self {
100        Error::V1Parse(format!("{:?}", err))
101    }
102}
103
104impl Debug for Error {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        write!(f, "{}", self)
107    }
108}
109
110type GenError = Box<dyn std::error::Error>;
111type GenResult<T> = Result<T, GenError>;