1pub mod read;
77pub mod records;
78pub mod write;
79
80mod io_utils;
81
82use std::{borrow::Cow, collections::BTreeMap, fmt, sync::Arc};
83
84use thiserror::Error;
85
86#[derive(Debug, Error)]
87pub enum McapError {
88 #[error("Bad magic number")]
89 BadMagic,
90 #[error("Footer record couldn't be found at the end of the file, before the magic bytes")]
91 BadFooter,
92 #[error("Attachment CRC failed (expeted {saved:08X}, got {calculated:08X}")]
93 BadAttachmentCrc { saved: u32, calculated: u32 },
94 #[error("Chunk CRC failed (expected {saved:08X}, got {calculated:08X}")]
95 BadChunkCrc { saved: u32, calculated: u32 },
96 #[error("Data section CRC failed (expected {saved:08X}, got {calculated:08X})")]
97 BadDataCrc { saved: u32, calculated: u32 },
98 #[error("Summary section CRC failed (expected {saved:08X}, got {calculated:08X})")]
99 BadSummaryCrc { saved: u32, calculated: u32 },
100 #[error("Index offset and length didn't point to the expected record type")]
101 BadIndex,
102 #[error("Channel `{0}` has mulitple records that don't match.")]
103 ConflictingChannels(String),
104 #[error("Schema `{0}` has mulitple records that don't match.")]
105 ConflictingSchemas(String),
106 #[error("Record parse failed")]
107 Parse(#[from] binrw::Error),
108 #[error("I/O error from writing, or reading a compression stream")]
109 Io(#[from] std::io::Error),
110 #[error("Schema has an ID of 0")]
111 InvalidSchemaId,
112 #[error("MCAP file ended in the middle of a record")]
113 UnexpectedEof,
114 #[error("Chunk ended in the middle of a record")]
115 UnexpectedEoc,
116 #[error("Message {0} referenced unknown channel {1}")]
117 UnknownChannel(u32, u16),
118 #[error("Channel `{0}` referenced unknown schema {1}")]
119 UnknownSchema(String, u16),
120 #[error("Found record with opcode {0:02X} in a chunk")]
121 UnexpectedChunkRecord(u8),
122 #[error("Unsupported compression format `{0}`")]
123 UnsupportedCompression(String),
124}
125
126pub type McapResult<T> = Result<T, McapError>;
127
128pub const MAGIC: &[u8] = &[0x89, b'M', b'C', b'A', b'P', 0x30, b'\r', b'\n'];
130
131#[derive(Debug, Copy, Clone, Default)]
133pub enum Compression {
134 #[default]
135 Zstd,
136 Lz4,
137}
138
139#[derive(Clone, PartialEq, Eq, Hash)]
144pub struct Schema<'a> {
145 pub name: String,
146 pub encoding: String,
147 pub data: Cow<'a, [u8]>,
148}
149
150impl fmt::Debug for Schema<'_> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 f.debug_struct("Schema")
153 .field("name", &self.name)
154 .field("encoding", &self.encoding)
155 .finish_non_exhaustive()
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Hash)]
161pub struct Channel<'a> {
162 pub topic: String,
163 pub schema: Option<Arc<Schema<'a>>>,
164
165 pub message_encoding: String,
166 pub metadata: BTreeMap<String, String>,
167}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
174pub struct Message<'a> {
175 pub channel: Arc<Channel<'a>>,
176 pub sequence: u32,
177 pub log_time: u64,
178 pub publish_time: u64,
179 pub data: Cow<'a, [u8]>,
180}
181
182#[derive(Debug, PartialEq, Eq)]
184pub struct Attachment<'a> {
185 pub log_time: u64,
186 pub create_time: u64,
187 pub name: String,
188 pub content_type: String,
189 pub data: Cow<'a, [u8]>,
190}
191
192pub use read::{MessageStream, Summary};
193pub use write::{WriteOptions, Writer};