mdf4_rs/
lib.rs

1#![forbid(unsafe_code)]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4//! # mdf4-rs
5//!
6//! A Rust library for reading and writing ASAM MDF 4 (Measurement Data Format) files.
7//!
8//! MDF4 is a binary file format standardized by ASAM for storing measurement data,
9//! commonly used in automotive and industrial applications for recording sensor data,
10//! CAN bus messages, and other time-series measurements.
11//!
12//! ## Features
13//!
14//! - **100% safe Rust** - `#![forbid(unsafe_code)]`
15//! - **no_std support** - Works on embedded targets with `alloc`
16//! - **Reading** (std only): Parse MDF4 files and access channel data
17//! - **Writing**: Create new MDF4 files (works with `alloc` only)
18//! - **Indexing** (std only): Generate lightweight JSON indexes
19//! - **Cutting** (std only): Extract time-based segments from recordings
20//! - **Merging** (std only): Combine multiple MDF files
21//!
22//! ## Feature Flags
23//!
24//! | Feature | Default | Description |
25//! |---------|---------|-------------|
26//! | `std` | Yes | Full std library support. Enables file I/O, indexing, merging. |
27//! | `alloc` | Yes | Heap allocation. Required for all functionality. |
28//!
29//! ## no_std Usage
30//!
31//! For embedded targets, disable default features and enable `alloc`:
32//!
33//! ```toml
34//! [dependencies]
35//! mdf4-rs = { version = "0.1", default-features = false, features = ["alloc"] }
36//! ```
37//!
38//! With `alloc` only, you can:
39//! - Create MDF files in memory using `MdfWriter::from_writer()`
40//! - Serialize blocks to byte vectors
41//! - Use all block types and data encoding
42//!
43//! ## Quick Start
44//!
45#![cfg_attr(feature = "std", doc = "### Reading an MDF file")]
46#![cfg_attr(feature = "std", doc = "")]
47#![cfg_attr(feature = "std", doc = "```no_run")]
48#![cfg_attr(feature = "std", doc = "use mdf4_rs::{MDF, Result};")]
49#![cfg_attr(feature = "std", doc = "")]
50#![cfg_attr(feature = "std", doc = "fn main() -> Result<()> {")]
51#![cfg_attr(
52    feature = "std",
53    doc = "    let mdf = MDF::from_file(\"recording.mf4\")?;"
54)]
55#![cfg_attr(feature = "std", doc = "")]
56#![cfg_attr(feature = "std", doc = "    for group in mdf.channel_groups() {")]
57#![cfg_attr(
58    feature = "std",
59    doc = "        println!(\"Group: {:?}\", group.name()?);"
60)]
61#![cfg_attr(feature = "std", doc = "")]
62#![cfg_attr(feature = "std", doc = "        for channel in group.channels() {")]
63#![cfg_attr(
64    feature = "std",
65    doc = "            let name = channel.name()?.unwrap_or_default();"
66)]
67#![cfg_attr(feature = "std", doc = "            let values = channel.values()?;")]
68#![cfg_attr(
69    feature = "std",
70    doc = "            let valid_count = values.iter().filter(|v| v.is_some()).count();"
71)]
72#![cfg_attr(
73    feature = "std",
74    doc = "            println!(\"  {}: {} valid samples\", name, valid_count);"
75)]
76#![cfg_attr(feature = "std", doc = "        }")]
77#![cfg_attr(feature = "std", doc = "    }")]
78#![cfg_attr(feature = "std", doc = "    Ok(())")]
79#![cfg_attr(feature = "std", doc = "}")]
80#![cfg_attr(feature = "std", doc = "```")]
81//!
82//! ### Writing an MDF file
83//!
84//! ```ignore
85//! use mdf4_rs::{MdfWriter, DataType, DecodedValue, Result};
86//!
87//! fn main() -> Result<()> {
88//!     let mut writer = MdfWriter::new("output.mf4")?;
89//!     writer.init_mdf_file()?;
90//!
91//!     // Create a channel group
92//!     let cg = writer.add_channel_group(None, |_| {})?;
93//!
94//!     // Add a temperature channel
95//!     writer.add_channel(&cg, None, |ch| {
96//!         ch.data_type = DataType::FloatLE;
97//!         ch.name = Some("Temperature".into());
98//!         ch.bit_count = 64;
99//!     })?;
100//!
101//!     // Write data records
102//!     writer.start_data_block_for_cg(&cg, 0)?;
103//!     for temp in [20.5, 21.0, 21.5, 22.0] {
104//!         writer.write_record(&cg, &[DecodedValue::Float(temp)])?;
105//!     }
106//!     writer.finish_data_block(&cg)?;
107//!     writer.finalize()?;
108//!
109//!     Ok(())
110//! }
111//! ```
112//!
113#![cfg_attr(feature = "std", doc = "### Using the Index for Efficient Access")]
114#![cfg_attr(feature = "std", doc = "")]
115#![cfg_attr(feature = "std", doc = "```no_run")]
116#![cfg_attr(
117    feature = "std",
118    doc = "use mdf4_rs::{MdfIndex, FileRangeReader, Result};"
119)]
120#![cfg_attr(feature = "std", doc = "")]
121#![cfg_attr(feature = "std", doc = "fn main() -> Result<()> {")]
122#![cfg_attr(feature = "std", doc = "    // Create an index from a file")]
123#![cfg_attr(
124    feature = "std",
125    doc = "    let index = MdfIndex::from_file(\"recording.mf4\")?;"
126)]
127#![cfg_attr(feature = "std", doc = "")]
128#![cfg_attr(feature = "std", doc = "    // Save index for later use")]
129#![cfg_attr(
130    feature = "std",
131    doc = "    index.save_to_file(\"recording.mdf4.index\")?;"
132)]
133#![cfg_attr(feature = "std", doc = "")]
134#![cfg_attr(feature = "std", doc = "    // Load index and read specific channel")]
135#![cfg_attr(
136    feature = "std",
137    doc = "    let index = MdfIndex::load_from_file(\"recording.mdf4.index\")?;"
138)]
139#![cfg_attr(
140    feature = "std",
141    doc = "    let mut reader = FileRangeReader::new(\"recording.mf4\")?;"
142)]
143#![cfg_attr(feature = "std", doc = "")]
144#![cfg_attr(
145    feature = "std",
146    doc = "    let values = index.read_channel_values_by_name(\"Temperature\", &mut reader)?;"
147)]
148#![cfg_attr(
149    feature = "std",
150    doc = "    println!(\"Read {} values\", values.len());"
151)]
152#![cfg_attr(feature = "std", doc = "")]
153#![cfg_attr(feature = "std", doc = "    Ok(())")]
154#![cfg_attr(feature = "std", doc = "}")]
155#![cfg_attr(feature = "std", doc = "```")]
156//!
157//! ## Module Overview
158//!
159//! | Module | Description | Requires |
160//! |--------|-------------|----------|
161//! | [`blocks`] | Low-level MDF block structures | `alloc` |
162//! | [`writer`] | MDF file creation | `alloc` |
163//! | [`parsing`] | File parsing utilities | `std` |
164//! | [`index`] | File indexing | `std` |
165//! | [`cut`] | Time-based segment extraction | `std` |
166//! | [`merge`] | File merging utilities | `std` |
167//! | [`error`] | Error types and [`Result`] alias | `alloc` |
168//!
169//! ## Error Handling
170//!
171//! All fallible operations return [`Result<T>`], which is an alias for
172//! `core::result::Result<T, Error>`. The [`Error`] enum covers I/O errors,
173//! parsing failures, and invalid file structures.
174
175#[cfg(feature = "alloc")]
176extern crate alloc;
177
178// Shared types available with alloc
179#[cfg(feature = "alloc")]
180mod types;
181
182// Modules available with alloc (writing support)
183#[cfg(feature = "alloc")]
184pub mod blocks;
185#[cfg(feature = "alloc")]
186pub mod error;
187#[cfg(feature = "alloc")]
188pub mod writer;
189
190// CAN bus integration (requires alloc, dbc-specific features gated inside)
191#[cfg(feature = "alloc")]
192pub mod can;
193
194// Modules requiring std (file I/O)
195#[cfg(feature = "std")]
196mod channel;
197#[cfg(feature = "std")]
198mod channel_group;
199#[cfg(feature = "std")]
200pub mod cut;
201#[cfg(feature = "std")]
202pub mod index;
203#[cfg(feature = "std")]
204mod mdf;
205#[cfg(feature = "std")]
206pub mod merge;
207#[cfg(feature = "std")]
208pub mod parsing;
209
210// Re-export commonly used types at the crate root
211#[cfg(feature = "alloc")]
212pub use blocks::DataType;
213#[cfg(feature = "alloc")]
214pub use error::{Error, Result};
215#[cfg(feature = "alloc")]
216pub use types::DecodedValue;
217#[cfg(feature = "alloc")]
218pub use writer::MdfWriter;
219#[cfg(feature = "alloc")]
220pub use writer::{FlushPolicy, StreamingConfig};
221
222#[cfg(feature = "std")]
223pub use channel::Channel;
224#[cfg(feature = "std")]
225pub use channel_group::ChannelGroup;
226#[cfg(feature = "std")]
227pub use cut::cut_mdf_by_time;
228#[cfg(feature = "std")]
229pub use index::{BufferedRangeReader, ByteRangeReader, FileRangeReader, MdfIndex};
230#[cfg(feature = "std")]
231pub use mdf::MDF;
232#[cfg(feature = "std")]
233pub use merge::merge_files;