From 99fa460a8c3b42b111620634a6120215ea4cc182 Mon Sep 17 00:00:00 2001 From: Zhexuan Yang Date: Sun, 8 Feb 2026 00:25:28 +0800 Subject: [PATCH 1/5] docs: update crates.io metadata and simplify public API - Add keywords, categories, and documentation links to Cargo.toml - Enhance lib.rs documentation with features table, examples, and feature flags - Add s3_example.rs demonstrating S3 remote file access - Add transform.rs demonstrating topic/type transformations - Update convert_format.rs to clarify same-format rewriting (not cross-format) - Remove HTTP/HTTPS references (not supported) - Fix ReaderConfig documentation (parallel processing, not auth) - Simplify public API: remove internal types from top-level exports - Update internal code to use explicit crate::core::* paths Users now access internal types via robocodec::core::{CodecValue, etc.} Public API now exports: - RoboReader, RoboWriter, RoboRewriter - ChannelInfo, DecodedMessageResult - TransformBuilder, TransformError - ReaderConfig, WriterConfig, RewriteOptions, RewriteStats - CodecError, Result --- Cargo.toml | 7 +- README.md | 50 ++-------- benches/decoder_bench.rs | 24 ++--- examples/README.md | 52 ++++++++-- examples/convert_format.rs | 54 +++++----- examples/s3_example.rs | 74 ++++++++++++++ examples/transform.rs | 82 +++++++++++++++ src/encoding/cdr/plan.rs | 6 +- src/encoding/json/decoder.rs | 2 +- src/encoding/protobuf/decoder.rs | 2 +- src/lib.rs | 165 ++++++++++++++++++++++++------- src/rewriter/engine.rs | 8 +- src/schema/ast.rs | 63 +++++++----- tests/common/mod.rs | 2 +- tests/mcap_integration_tests.rs | 2 +- tests/schema_resolution_test.rs | 16 +-- 16 files changed, 446 insertions(+), 163 deletions(-) create mode 100644 examples/s3_example.rs create mode 100644 examples/transform.rs diff --git a/Cargo.toml b/Cargo.toml index d2fea18..d1fb0b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,12 @@ version.workspace = true edition.workspace = true authors.workspace = true license.workspace = true -description = "High-performance robotics data codec library for reading, writing, and converting MCAP and ROS1 bag files with support for CDR, Protobuf, and JSON encodings" +description = "High-performance robotics data codec library for reading, writing, and converting MCAP, ROS1 bag, and RRF2 files" +keywords = ["robotics", "mcap", "ros", "rosbag", "codec"] +categories = ["parser-implementations", "encoding", "science::robotics"] +readme = "README.md" +repository = "https://github.com/archebase/robocodec" +documentation = "https://docs.rs/robocodec" [lib] crate-type = ["rlib", "cdylib"] diff --git a/README.md b/README.md index a1d5d1c..ccde159 100644 --- a/README.md +++ b/README.md @@ -122,48 +122,6 @@ export AWS_SECRET_ACCESS_KEY="your-oss-secret-key" > **Note:** While we use AWS-standard environment variable names for compatibility, robocodec works with any S3-compatible storage service. -### Read from HTTP/HTTPS - -Robocodec also supports reading directly from HTTP/HTTPS URLs: - -```rust -use robocodec::RoboReader; - -// Format detected from URL path, access via HTTP -let reader = RoboReader::open("https://example.com/data.mcap")?; -println!("Found {} channels", reader.channels().len()); -``` - -> **Note:** HTTP reading supports range requests for efficient access to large files. - -#### HTTP Authentication - -For authenticated HTTP endpoints, robocodec supports Bearer tokens and Basic authentication via `ReaderConfig`: - -```rust -use robocodec::io::{RoboReader, ReaderConfig}; - -// Bearer token (OAuth2/JWT) -let config = ReaderConfig::default().with_http_bearer_token("your-token-here"); -let reader = RoboReader::open_with_config("https://example.com/data.mcap", config)?; - -// Basic authentication -let config = ReaderConfig::default().with_http_basic_auth("username", "password"); -let reader = RoboReader::open_with_config("https://example.com/data.mcap", config)?; -``` - -Alternatively, you can provide authentication via URL query parameters: - -```rust -use robocodec::RoboReader; - -// Bearer token via URL -let reader = RoboReader::open("https://example.com/data.mcap?bearer_token=your-token")?; - -// Basic auth via URL (user:pass encoded) -let reader = RoboReader::open("https://example.com/data.mcap?basic_auth=user:pass")?; -``` - ### Write to S3 ```rust @@ -200,15 +158,19 @@ let reader = RoboReader::open( )?; ``` -### Convert between formats +### Rewrite files with transformations + +The rewriter processes files in the same format, optionally applying topic and type transformations: ```rust use robocodec::RoboRewriter; -let rewriter = RoboRewriter::open("input.bag")?; +let rewriter = RoboRewriter::open("input.mcap")?; rewriter.rewrite("output.mcap")?; ``` +**Note:** Cross-format conversion is not currently supported. Use the rewriter to transform data within the same format. + ### Rename topics during conversion ```rust diff --git a/benches/decoder_bench.rs b/benches/decoder_bench.rs index 5080caf..8c82e46 100644 --- a/benches/decoder_bench.rs +++ b/benches/decoder_bench.rs @@ -180,9 +180,9 @@ fn bench_value_operations(c: &mut Criterion) { // Benchmark string value access group.bench_function("access_string_value", |b| { - let value = robocodec::CodecValue::String("test string value".to_string()); + let value = robocodec::core::CodecValue::String("test string value".to_string()); b.iter(|| { - if let robocodec::CodecValue::String(s) = black_box(&value) { + if let robocodec::core::CodecValue::String(s) = black_box(&value) { black_box(s.len()); } }) @@ -190,15 +190,15 @@ fn bench_value_operations(c: &mut Criterion) { // Benchmark array value access group.bench_function("access_array_value", |b| { - let value = robocodec::CodecValue::Array(vec![ - robocodec::CodecValue::Int64(1), - robocodec::CodecValue::Int64(2), - robocodec::CodecValue::Int64(3), - robocodec::CodecValue::Int64(4), - robocodec::CodecValue::Int64(5), + let value = robocodec::core::CodecValue::Array(vec![ + robocodec::core::CodecValue::Int64(1), + robocodec::core::CodecValue::Int64(2), + robocodec::core::CodecValue::Int64(3), + robocodec::core::CodecValue::Int64(4), + robocodec::core::CodecValue::Int64(5), ]); b.iter(|| { - if let robocodec::CodecValue::Array(arr) = black_box(&value) { + if let robocodec::core::CodecValue::Array(arr) = black_box(&value) { black_box(arr.len()); } }) @@ -210,13 +210,13 @@ fn bench_value_operations(c: &mut Criterion) { for i in 0..10 { fields.insert( format!("field_{}", i), - robocodec::CodecValue::Int64(i as i64), + robocodec::core::CodecValue::Int64(i as i64), ); } - let value = robocodec::CodecValue::Struct(fields); + let value = robocodec::core::CodecValue::Struct(fields); b.iter(|| { - if let robocodec::CodecValue::Struct(fields) = black_box(&value) { + if let robocodec::core::CodecValue::Struct(fields) = black_box(&value) { black_box(fields.len()); } }) diff --git a/examples/README.md b/examples/README.md index 8d24a11..3efe8d7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,7 +4,7 @@ This directory contains practical examples demonstrating the public API of the r ## Running Examples -Each example accepts a file path as an argument: +### Local Files ```bash # Inspect a file @@ -13,8 +13,25 @@ cargo run --example read_file -- tests/fixtures/robocodec_test_14.mcap # Decode and display messages cargo run --example decode_messages -- tests/fixtures/robocodec_test_14.mcap -# Convert between formats -cargo run --example convert_format -- tests/fixtures/robocodec_test_14.mcap output.bag +# Rewrite a file (same format) +cargo run --example convert_format -- tests/fixtures/robocodec_test_14.mcap output.mcap + +# Transform topics and types +cargo run --example transform -- tests/fixtures/robocodec_test_14.mcap output.mcap +``` + +### Remote Files (S3) + +```bash +# Set S3 credentials (for AWS S3, MinIO, Alibaba OSS, etc.) +export AWS_ACCESS_KEY_ID="your-access-key" +export AWS_SECRET_ACCESS_KEY="your-secret-key" + +# Read from S3 +cargo run --example s3_example -- s3://my-bucket/path/to/data.mcap + +# Read from S3 with custom endpoint (MinIO, Alibaba OSS, etc.) +cargo run --example s3_example -- "s3://bucket/data.mcap?endpoint=http://localhost:9000" ``` ## Examples @@ -28,13 +45,15 @@ Demonstrates opening a robotics data file and inspecting its metadata, channels, - Accessing file metadata - Listing channels with their properties -### `convert_format.rs` - Format Conversion +### `convert_format.rs` - File Rewriting -Demonstrates converting between MCAP and ROS1 bag formats. +Demonstrates rewriting a robotics data file in the same format. **What you'll learn:** -- Using `RoboRewriter` for format conversion -- Understanding conversion statistics +- Using `RoboRewriter` to rewrite files +- Understanding rewrite statistics + +**Note:** The rewriter preserves the same format as the input file. Cross-format conversion is not currently supported. ### `decode_messages.rs` - Message Decoding @@ -44,6 +63,25 @@ Demonstrates iterating through decoded messages with timestamps. - Using the `decoded()` iterator - Accessing message data, timestamps, and channel info +### `s3_example.rs` - S3 Remote File Access + +Demonstrates reading robotics data files from S3-compatible storage. + +**What you'll learn:** +- Reading from S3-compatible storage (AWS S3, MinIO, Alibaba OSS, etc.) +- S3 authentication via environment variables +- Custom S3 endpoints via URL parameters + +### `transform.rs` - Topic and Type Transformations + +Demonstrates renaming topics and message types during format conversion. + +**What you'll learn:** +- Using `TransformBuilder` to create transformation pipelines +- Topic renaming with exact name matching +- Type renaming for schema migration +- Combining transformations with `RoboRewriter` + ## Public API The examples demonstrate the **public API** only: diff --git a/examples/convert_format.rs b/examples/convert_format.rs index 057e3fe..0b01ff7 100644 --- a/examples/convert_format.rs +++ b/examples/convert_format.rs @@ -2,19 +2,19 @@ // // SPDX-License-Identifier: MulanPSL-2.0 -//! Format conversion example. +//! File rewriting example. //! -//! Demonstrates converting between robotics data formats (MCAP ↔ ROS1 bag) -//! using the unified RoboRewriter API. +//! Demonstrates rewriting a robotics data file with the same format. +//! The rewriter can apply topic and type transformations during the process. //! //! # Usage //! //! ```bash -//! # Convert MCAP to ROS1 bag -//! cargo run --example convert_format -- input.mcap output.bag +//! # Rewrite MCAP file (same format, can apply transformations) +//! cargo run --example convert_format -- input.mcap output.mcap //! -//! # Convert ROS1 bag to MCAP -//! cargo run --example convert_format -- input.bag output.mcap +//! # Rewrite ROS1 bag file (same format, can apply transformations) +//! cargo run --example convert_format -- input.bag output.bag //! ``` use robocodec::RoboRewriter; @@ -26,37 +26,45 @@ fn main() -> Result<(), Box> { if args.len() != 3 { eprintln!("Usage: cargo run --example convert_format -- "); eprintln!(); + eprintln!("Rewrites a file in the same format. Output format must match input format."); + eprintln!(); eprintln!("Examples:"); - eprintln!(" cargo run --example convert_format -- input.mcap output.bag"); - eprintln!(" cargo run --example convert_format -- input.bag output.mcap"); + eprintln!(" cargo run --example convert_format -- input.mcap output.mcap"); + eprintln!(" cargo run --example convert_format -- input.bag output.bag"); + eprintln!(); + eprintln!("Note: See transform.rs for examples of applying topic/type transformations."); std::process::exit(1); } let input_path = &args[1]; let output_path = &args[2]; - println!("🔄 Converting {} → {}", input_path, output_path); + // Validate that input and output have the same extension + let input_ext = input_path.rsplit('.').next().unwrap_or(""); + let output_ext = output_path.rsplit('.').next().unwrap_or(""); + + if input_ext != output_ext { + eprintln!("Error: Input and output formats must match."); + eprintln!(" Input format: {}", input_ext); + eprintln!(" Output format: {}", output_ext); + eprintln!(); + eprintln!("Note: Cross-format conversion is not currently supported."); + eprintln!(" The rewriter preserves the same format as the input file."); + std::process::exit(1); + } + + println!("🔄 Rewriting {} → {}", input_path, output_path); // Create rewriter (format auto-detected from input) let mut rewriter = RoboRewriter::open(input_path)?; - println!(" Input format: {:?}", input_path.rsplit('.').next()); + println!(" Format: {}", input_ext.to_uppercase()); println!(" Input: {}", rewriter.input_path().display()); - // Detect output format from extension - let output_format = if output_path.ends_with(".mcap") { - "MCAP" - } else if output_path.ends_with(".bag") { - "ROS1 Bag" - } else { - "Unknown" - }; - println!(" Output format: {}", output_format); - - // Convert + // Rewrite (same format) let stats = rewriter.rewrite(output_path)?; println!(); - println!("✅ Conversion complete!"); + println!("✅ Rewrite complete!"); println!(" Messages processed: {}", stats.message_count); println!(" Channels processed: {}", stats.channel_count); if stats.decode_failures > 0 { diff --git a/examples/s3_example.rs b/examples/s3_example.rs new file mode 100644 index 0000000..e521386 --- /dev/null +++ b/examples/s3_example.rs @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2026 ArcheBase +// +// SPDX-License-Identifier: MulanPSL-2.0 + +//! S3 remote file access example. +//! +//! Demonstrates reading robotics data files from S3-compatible storage +//! including AWS S3, MinIO, Alibaba OSS, and other S3-compatible services. +//! +//! # Usage +//! +//! ```bash +//! # S3 (requires credentials via environment variables) +//! export AWS_ACCESS_KEY_ID="your-access-key" +//! export AWS_SECRET_ACCESS_KEY="your-secret-key" +//! cargo run --example s3_example -- s3://my-bucket/path/to/data.mcap +//! +//! # S3 with custom endpoint (MinIO, Alibaba OSS, etc.) +//! cargo run --example s3_example -- "s3://bucket/data.mcap?endpoint=http://localhost:9000" +//! ``` + +use robocodec::{FormatReader, RoboReader}; +use std::env; + +fn main() -> Result<(), Box> { + let args: Vec = env::args().collect(); + + if args.len() != 2 { + eprintln!("Usage: cargo run --example s3_example -- "); + eprintln!(); + eprintln!("Examples:"); + eprintln!(" cargo run --example s3_example -- s3://my-bucket/path/to/data.mcap"); + eprintln!( + " cargo run --example s3_example -- s3://bucket/data.mcap?endpoint=http://localhost:9000" + ); + eprintln!(); + eprintln!("S3 credentials (via environment variables):"); + eprintln!(" AWS_ACCESS_KEY_ID"); + eprintln!(" AWS_SECRET_ACCESS_KEY"); + eprintln!(" AWS_REGION (optional, defaults to us-east-1)"); + std::process::exit(1); + } + + let url = &args[1]; + + if !url.starts_with("s3://") { + eprintln!("Error: Only s3:// URLs are supported"); + eprintln!("Got: {}", url); + std::process::exit(1); + } + + println!("🌐 Opening: {}", url); + + // Format and transport auto-detected from URL scheme + let reader = RoboReader::open(url)?; + + println!("📊 Format: {:?}", reader.format()); + println!("📝 Channels: {}", reader.channels().len()); + println!("💬 Total messages: {}", reader.message_count()); + println!(); + + println!("─── Channels ───"); + for channel in reader.channels().values() { + println!( + " • {} ({}) - {} messages", + channel.topic, channel.message_type, channel.message_count + ); + } + + println!(); + println!("✅ Successfully accessed S3 file!"); + + Ok(()) +} diff --git a/examples/transform.rs b/examples/transform.rs new file mode 100644 index 0000000..80607c3 --- /dev/null +++ b/examples/transform.rs @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2026 ArcheBase +// +// SPDX-License-Identifier: MulanPSL-2.0 + +//! Topic and type transformation example. +//! +//! Demonstrates renaming topics and message types during file rewriting. +//! The rewriter preserves the same format but can transform schema and metadata. +//! +//! # Usage +//! +//! ```bash +//! # Transform topics while rewriting (same format) +//! cargo run --example transform -- input.mcap output.mcap +//! ``` + +use robocodec::{RewriteOptions, RoboRewriter, TransformBuilder}; +use std::env; + +fn main() -> Result<(), Box> { + let args: Vec = env::args().collect(); + + if args.len() != 3 { + eprintln!("Usage: cargo run --example transform -- "); + eprintln!(); + eprintln!( + "Rewrites a file with topic/type transformations. Output format must match input format." + ); + eprintln!(); + eprintln!("Example:"); + eprintln!(" cargo run --example transform -- input.mcap output.mcap"); + std::process::exit(1); + } + + let input_path = &args[1]; + let output_path = &args[2]; + + println!("🔄 Transforming: {} → {}", input_path, output_path); + + // Build transformations: rename topics and types + let transform = TransformBuilder::new() + // Rename a specific topic + .with_topic_rename("/old_camera/image_raw", "/camera/image") + // Rename another topic (wildcard prefix not supported, use explicit renames) + .with_topic_rename("/lidar/points_old", "/lidar/points") + // Rename message types + .with_type_rename("sensor_msgs/PointCloud2", "custom_msgs/PointCloud") + .build(); + + println!(); + println!("─── Transformations ───"); + println!(" Topic renames:"); + println!(" /old_camera/image_raw → /camera/image"); + println!(" /lidar/points_old → /lidar/points"); + println!(" Type renames:"); + println!(" sensor_msgs/PointCloud2 → custom_msgs/PointCloud"); + + // Create rewriter with transformations + let options = RewriteOptions::default().with_transforms(transform); + let mut rewriter = RoboRewriter::with_options(input_path, options)?; + + println!(); + println!(" Input format: {:?}", input_path.rsplit('.').next()); + println!(" Input: {}", rewriter.input_path().display()); + + // Apply transformations and convert + let stats = rewriter.rewrite(output_path)?; + + println!(); + println!("✅ Transformation complete!"); + println!(" Messages processed: {}", stats.message_count); + println!(" Channels processed: {}", stats.channel_count); + + if stats.decode_failures > 0 { + println!(" ⚠️ Decode failures: {}", stats.decode_failures); + } + if stats.encode_failures > 0 { + println!(" ⚠️ Encode failures: {}", stats.encode_failures); + } + + Ok(()) +} diff --git a/src/encoding/cdr/plan.rs b/src/encoding/cdr/plan.rs index cacf84e..a6ad630 100644 --- a/src/encoding/cdr/plan.rs +++ b/src/encoding/cdr/plan.rs @@ -24,7 +24,7 @@ pub enum DecodeOp { /// Field path for the value (e.g., "position.x") field_path: String, /// Primitive type to read - type_name: crate::PrimitiveType, + type_name: crate::core::PrimitiveType, }, /// Read a string value (4-byte length prefix + UTF-8 bytes). @@ -77,7 +77,7 @@ pub enum DecodeOp { #[derive(Debug, Clone, PartialEq)] pub enum ElementType { /// Primitive element - Primitive(crate::PrimitiveType), + Primitive(crate::core::PrimitiveType), /// String element (for string arrays) String, /// Bytes element (for bytes arrays) @@ -148,7 +148,7 @@ impl fmt::Display for DecodePlan { #[cfg(test)] mod tests { use super::*; - use crate::PrimitiveType; + use crate::core::PrimitiveType; #[test] fn test_decode_op_align_variant() { diff --git a/src/encoding/json/decoder.rs b/src/encoding/json/decoder.rs index f3bbc3d..32d266d 100644 --- a/src/encoding/json/decoder.rs +++ b/src/encoding/json/decoder.rs @@ -20,7 +20,7 @@ use std::collections::HashMap; -use crate::{CodecError, CodecValue, DecodedMessage, Result as CoreResult}; +use crate::core::{CodecError, CodecValue, DecodedMessage, Result as CoreResult}; /// JSON decoder for decoding JSON data. pub struct JsonDecoder { diff --git a/src/encoding/protobuf/decoder.rs b/src/encoding/protobuf/decoder.rs index e5617ec..1f9aaa4 100644 --- a/src/encoding/protobuf/decoder.rs +++ b/src/encoding/protobuf/decoder.rs @@ -24,7 +24,7 @@ use std::collections::HashMap; -use crate::{CodecError, CodecValue, DecodedMessage, Result as CoreResult}; +use crate::core::{CodecError, CodecValue, DecodedMessage, Result as CoreResult}; /// Protobuf decoder for decoding protobuf binary data. pub struct ProtobufDecoder { diff --git a/src/lib.rs b/src/lib.rs index 7cb70dc..2a54e68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,75 +4,168 @@ //! # Robocodec //! -//! Robotics data format library for MCAP and ROS bag files. +//! **Robocodec** is a high-performance robotics data codec library for reading, writing, +//! and converting robotics data files. It provides a unified, format-agnostic API with +//! automatic format detection and support for multiple encodings and schema types. //! -//! This library provides a unified interface for reading and writing robotics data files: -//! - **[`RoboReader`]** - Auto-detects format and provides unified message iteration -//! - **[`RoboWriter`]** - Auto-detects format from extension -//! - **[`RoboRewriter`]** - Unified rewriter with format auto-detection -//! - **[`TransformBuilder`]** - Topic/type renaming and transformations +//! ## Features //! -//! ## Example: Reading with Auto-Detection +//! - **Unified API** - Single interface for MCAP, ROS1 bag, and RRF2 formats +//! - **Auto-Detection** - Format detected from file extension or URL scheme +//! - **Remote Support** - First-class S3 support with streaming +//! - **Fast** - Parallel processing with rayon, zero-copy memory-mapped files +//! - **Transformations** - Topic/type renaming and format conversion built-in +//! - **Schema Support** - ROS `.msg`, ROS2 IDL, and OMG IDL +//! - **Encodings** - CDR, Protobuf, and JSON +//! +//! ## Supported Formats +//! +//! | Format | Read | Write | Notes | +//! |:--------|:-----|:-------|:------| +//! | MCAP | ✅ | ✅ | Robotics message format | +//! | ROS1 Bag | ✅ | ✅ | ROS1 legacy format | +//! | RRF2 | ✅ | ✅ | Rerun format (0.27+) | +//! +//! ## Quick Start +//! +//! ### Reading Messages //! //! ```rust,no_run //! # fn main() -> Result<(), Box> { //! use robocodec::RoboReader; -//! use robocodec::io::FormatReader; //! -//! // Format auto-detected +//! // Format auto-detected from extension //! let reader = RoboReader::open("file.mcap")?; +//! +//! // Inspect file metadata //! println!("Channels: {}", reader.channels().len()); +//! println!("Messages: {}", reader.message_count()); //! -//! // Iterate over decoded messages +//! // Iterate over decoded messages with timestamps //! for result in reader.decoded()? { -//! let decoded = result?; -//! println!("Topic: {}", decoded.topic()); -//! println!("Data: {:?}", decoded.message); +//! let msg = result?; +//! println!("{}: {}", msg.channel.topic, msg.message.len()); //! } //! # Ok(()) //! # } //! ``` //! -//! ## Example: Writing with Auto-Detection +//! ### Writing Messages //! //! ```rust,no_run //! # fn main() -> Result<(), Box> { -//! use robocodec::io::FormatWriter; //! use robocodec::RoboWriter; //! -//! // Format detected from extension (.mcap or .bag) +//! // Format detected from extension (.mcap, .bag, or .rrd) //! let mut writer = RoboWriter::create("output.mcap")?; -//! let channel_id = writer.add_channel("/topic", "type", "cdr", None)?; +//! +//! // Add a channel and write messages +//! let channel_id = writer.add_channel("/topic", "MessageType", "cdr", None)?; +//! // ... write messages ... //! writer.finish()?; //! # Ok(()) //! # } //! ``` //! -//! ## Example: S3 Support -//! -//! For reading from S3-compatible storage: +//! ### Reading from S3 //! //! ```rust,no_run //! # fn main() -> Result<(), Box> { //! use robocodec::RoboReader; //! -//! // Read directly from S3 +//! // Read directly from S3 (requires `remote` feature, enabled by default) //! let reader = RoboReader::open("s3://my-bucket/path/to/data.mcap")?; +//! println!("Found {} channels", reader.channels().len()); +//! +//! // Custom endpoint (MinIO, Alibaba OSS, etc.) +//! let reader = RoboReader::open( +//! "s3://bucket/data.mcap?endpoint=https://oss-cn-hangzhou.aliyuncs.com" +//! )?; //! # Ok(()) //! # } //! ``` //! -//! ## Example: Rewriting with Transformations +//! ### Rewriting with Transformations +//! +//! Rewrite a file while applying topic and type transformations: //! //! ```rust,no_run //! # fn main() -> Result<(), Box> { //! use robocodec::RoboRewriter; //! +//! // Format detected from input extension //! let mut rewriter = RoboRewriter::open("input.mcap")?; +//! let stats = rewriter.rewrite("output.mcap")?; +//! println!("Processed {} messages", stats.message_count); +//! # Ok(()) +//! # } +//! ``` +//! +//! ### Topic and Type Transformations +//! +//! ```rust,no_run +//! # fn main() -> Result<(), Box> { +//! use robocodec::{RoboRewriter, TransformBuilder, RewriteOptions}; +//! +//! // Rename topics and types during conversion +//! let transform = TransformBuilder::new() +//! .with_topic_rename("/old/topic", "/new/topic") +//! .with_type_rename("OldType", "NewType") +//! .build(); +//! +//! let options = RewriteOptions::default().with_transforms(transform); +//! let mut rewriter = RoboRewriter::with_options("input.mcap", options)?; //! rewriter.rewrite("output.mcap")?; //! # Ok(()) //! # } //! ``` +//! +//! ## Feature Flags +//! +//! | Feature | Default | Description | +//! |:--------|:--------|:------------| +//! | `remote` | ✅ Yes | S3 and HTTP/HTTPS transport support | +//! | `python` | ❌ No | Python bindings via PyO3 | +//! | `jemalloc` | ❌ No | Use jemalloc allocator (Linux only) | +//! +//! Disable default features: +//! +//! ```toml +//! [dependencies] +//! robocodec = { version = "0.1", default-features = false } +//! ``` +//! +//! Enable specific features: +//! +//! ```toml +//! [dependencies] +//! robocodec = { version = "0.1", features = ["python", "jemalloc"] } +//! ``` +//! +//! ## Public API +//! +//! The library exports these key types: +//! +//! - **[`RoboReader`]** - Unified reader with format auto-detection +//! - **[`RoboWriter`]** - Unified writer with format auto-detection +//! - **[`RoboRewriter`]** - Unified rewriter for format conversion +//! - **[`TransformBuilder`]** - Builder for topic/type transformations +//! - **[`DecodedMessageResult`]** - Message data with metadata and timestamps +//! - **[`ChannelInfo`]** - Channel/topic metadata +//! - **[`ReaderConfig`]** - Configuration for readers (parallel processing, chunk merging) +//! - **[`WriterConfig`]** - Configuration for writers +//! +//! ## S3 Authentication +//! +//! For S3-compatible storage, set credentials via environment variables: +//! +//! ```bash +//! export AWS_ACCESS_KEY_ID="your-access-key" +//! export AWS_SECRET_ACCESS_KEY="your-secret-key" +//! export AWS_REGION="us-east-1" // optional, defaults to us-east-1 +//! ``` +//! +//! Works with AWS S3, Alibaba Cloud OSS, MinIO, and other S3-compatible services. // Core types // @@ -102,8 +195,8 @@ pub mod core; -// Re-export core types for convenience -pub use core::{CodecError, CodecValue, DecodedMessage, Encoding, PrimitiveType, Result}; +// Re-export core error type for public API +pub use core::{CodecError, Result}; // Encoding/decoding (hidden from docs but available for advanced use) #[doc(hidden)] @@ -123,7 +216,7 @@ pub mod io; // Re-export key public API types at top level pub use io::RoboReader; pub use io::metadata::{ChannelInfo, DecodedMessageResult}; -pub use io::reader::{DecodedMessageIter, ReaderConfig}; +pub use io::reader::ReaderConfig; pub use io::writer::{RoboWriter, WriterConfig}; // Format traits are available but hidden from documentation @@ -133,15 +226,14 @@ pub use io::traits::FormatReader; #[doc(hidden)] pub use io::traits::FormatWriter; -// Rewriter support (shared types and traits) +// Rewriter support pub mod rewriter; -pub use rewriter::{FormatRewriter, RewriteOptions, RewriteStats, RoboRewriter}; +// Public rewriter API - keep only what users need +pub use rewriter::{RewriteOptions, RewriteStats, RoboRewriter}; -pub use transform::{ - MultiTransform, TopicRenameTransform, TransformBuilder, TransformError, TransformedChannel, - TypeNormalization, TypeRenameTransform, -}; +// Transformation builder and error type only +pub use transform::{TransformBuilder, TransformError}; // Format-specific modules are private implementation details // Use RoboReader/RoboWriter for a unified interface @@ -154,7 +246,8 @@ pub use transform::{ /// # Example /// /// ```no_run -/// # use robocodec::{Decoder, DecodedMessage, CodecError}; +/// # use robocodec::{Decoder, CodecError}; +/// # use robocodec::core::DecodedMessage; /// # struct MyDecoder; /// # impl Decoder for MyDecoder { /// # fn decode(&self, data: &[u8], schema: &str, type_name: Option<&str>) -> Result { @@ -187,13 +280,19 @@ pub trait Decoder: Send + Sync { /// /// ```no_run /// # use robocodec::{Decoder, CodecError}; + /// # use robocodec::core::DecodedMessage; /// # fn test(decoder: &dyn Decoder, data: &[u8]) -> Result<(), CodecError> { /// let schema = "int32 value\nstring name"; /// let message = decoder.decode(data, schema, Some("test/Type"))?; /// # Ok(()) /// # } /// ``` - fn decode(&self, data: &[u8], schema: &str, type_name: Option<&str>) -> Result; + fn decode( + &self, + data: &[u8], + schema: &str, + type_name: Option<&str>, + ) -> Result; } // Python bindings (optional feature) diff --git a/src/rewriter/engine.rs b/src/rewriter/engine.rs index c7efc2a..4a63dff 100644 --- a/src/rewriter/engine.rs +++ b/src/rewriter/engine.rs @@ -263,10 +263,10 @@ impl McapRewriteEngine { fn create_schema_metadata( &self, channel: &ChannelInfo, - encoding: &crate::Encoding, + encoding: &crate::core::Encoding, ) -> Result { match encoding { - crate::Encoding::Cdr => { + crate::core::Encoding::Cdr => { let schema_text = channel .schema .as_ref() @@ -284,7 +284,7 @@ impl McapRewriteEngine { schema_encoding, )) } - crate::Encoding::Protobuf => { + crate::core::Encoding::Protobuf => { let schema_data = channel .schema_data .as_ref() @@ -300,7 +300,7 @@ impl McapRewriteEngine { schema_data, )) } - crate::Encoding::Json => { + crate::core::Encoding::Json => { let schema_text = channel .schema .as_ref() diff --git a/src/schema/ast.rs b/src/schema/ast.rs index e1aa718..244c255 100644 --- a/src/schema/ast.rs +++ b/src/schema/ast.rs @@ -154,22 +154,22 @@ impl PrimitiveType { /// Convert to the core `PrimitiveType`. #[must_use] - pub fn to_core(self) -> crate::PrimitiveType { + pub fn to_core(self) -> crate::core::PrimitiveType { match self { - PrimitiveType::Bool => crate::PrimitiveType::Bool, - PrimitiveType::Int8 => crate::PrimitiveType::Int8, - PrimitiveType::Int16 => crate::PrimitiveType::Int16, - PrimitiveType::Int32 => crate::PrimitiveType::Int32, - PrimitiveType::Int64 => crate::PrimitiveType::Int64, - PrimitiveType::UInt8 => crate::PrimitiveType::UInt8, - PrimitiveType::UInt16 => crate::PrimitiveType::UInt16, - PrimitiveType::UInt32 => crate::PrimitiveType::UInt32, - PrimitiveType::UInt64 => crate::PrimitiveType::UInt64, - PrimitiveType::Float32 => crate::PrimitiveType::Float32, - PrimitiveType::Float64 => crate::PrimitiveType::Float64, - PrimitiveType::String | PrimitiveType::WString => crate::PrimitiveType::String, - PrimitiveType::Byte | PrimitiveType::Char => crate::PrimitiveType::Byte, - PrimitiveType::Time | PrimitiveType::Duration => crate::PrimitiveType::Int64, // Fallback + PrimitiveType::Bool => crate::core::PrimitiveType::Bool, + PrimitiveType::Int8 => crate::core::PrimitiveType::Int8, + PrimitiveType::Int16 => crate::core::PrimitiveType::Int16, + PrimitiveType::Int32 => crate::core::PrimitiveType::Int32, + PrimitiveType::Int64 => crate::core::PrimitiveType::Int64, + PrimitiveType::UInt8 => crate::core::PrimitiveType::UInt8, + PrimitiveType::UInt16 => crate::core::PrimitiveType::UInt16, + PrimitiveType::UInt32 => crate::core::PrimitiveType::UInt32, + PrimitiveType::UInt64 => crate::core::PrimitiveType::UInt64, + PrimitiveType::Float32 => crate::core::PrimitiveType::Float32, + PrimitiveType::Float64 => crate::core::PrimitiveType::Float64, + PrimitiveType::String | PrimitiveType::WString => crate::core::PrimitiveType::String, + PrimitiveType::Byte | PrimitiveType::Char => crate::core::PrimitiveType::Byte, + PrimitiveType::Time | PrimitiveType::Duration => crate::core::PrimitiveType::Int64, // Fallback } } } @@ -586,11 +586,17 @@ mod tests { #[test] fn test_primitive_type_to_core_basic() { - assert_eq!(PrimitiveType::Bool.to_core(), crate::PrimitiveType::Bool); - assert_eq!(PrimitiveType::Int32.to_core(), crate::PrimitiveType::Int32); + assert_eq!( + PrimitiveType::Bool.to_core(), + crate::core::PrimitiveType::Bool + ); + assert_eq!( + PrimitiveType::Int32.to_core(), + crate::core::PrimitiveType::Int32 + ); assert_eq!( PrimitiveType::Float64.to_core(), - crate::PrimitiveType::Float64 + crate::core::PrimitiveType::Float64 ); } @@ -598,27 +604,36 @@ mod tests { fn test_primitive_type_to_core_string() { assert_eq!( PrimitiveType::String.to_core(), - crate::PrimitiveType::String + crate::core::PrimitiveType::String ); assert_eq!( PrimitiveType::WString.to_core(), - crate::PrimitiveType::String + crate::core::PrimitiveType::String ); } #[test] fn test_primitive_type_to_core_byte_char() { - assert_eq!(PrimitiveType::Byte.to_core(), crate::PrimitiveType::Byte); - assert_eq!(PrimitiveType::Char.to_core(), crate::PrimitiveType::Byte); + assert_eq!( + PrimitiveType::Byte.to_core(), + crate::core::PrimitiveType::Byte + ); + assert_eq!( + PrimitiveType::Char.to_core(), + crate::core::PrimitiveType::Byte + ); } #[test] fn test_primitive_type_to_core_time_duration() { // Time and Duration fallback to Int64 - assert_eq!(PrimitiveType::Time.to_core(), crate::PrimitiveType::Int64); + assert_eq!( + PrimitiveType::Time.to_core(), + crate::core::PrimitiveType::Int64 + ); assert_eq!( PrimitiveType::Duration.to_core(), - crate::PrimitiveType::Int64 + crate::core::PrimitiveType::Int64 ); } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index e1509dd..cd4723f 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; -use robocodec::CodecValue; +use robocodec::core::CodecValue; // ============================================================================ // Test Expectations diff --git a/tests/mcap_integration_tests.rs b/tests/mcap_integration_tests.rs index 0c998c8..85e6f1e 100644 --- a/tests/mcap_integration_tests.rs +++ b/tests/mcap_integration_tests.rs @@ -13,8 +13,8 @@ use common::*; use std::collections::HashMap; use std::path::Path; -use robocodec::CodecValue; use robocodec::FormatReader; +use robocodec::core::CodecValue; use robocodec::io::RoboReader; /// Path to the fixtures directory. diff --git a/tests/schema_resolution_test.rs b/tests/schema_resolution_test.rs index 506621e..014aa6b 100644 --- a/tests/schema_resolution_test.rs +++ b/tests/schema_resolution_test.rs @@ -209,30 +209,30 @@ uint32 nanosec assert!(result.contains_key("effort"), "Should have effort field"); // Verify the decoded float64 values are correct - if let Some(robocodec::CodecValue::Array(positions)) = result.get("position") { + if let Some(robocodec::core::CodecValue::Array(positions)) = result.get("position") { assert_eq!(positions.len(), 2, "Should have 2 positions"); - if let robocodec::CodecValue::Float64(v) = &positions[0] { + if let robocodec::core::CodecValue::Float64(v) = &positions[0] { assert_eq!(*v, 1.0, "position[0] should be 1.0"); } - if let robocodec::CodecValue::Float64(v) = &positions[1] { + if let robocodec::core::CodecValue::Float64(v) = &positions[1] { assert_eq!(*v, 2.0, "position[1] should be 2.0"); } } - if let Some(robocodec::CodecValue::Array(velocities)) = result.get("velocity") { + if let Some(robocodec::core::CodecValue::Array(velocities)) = result.get("velocity") { assert_eq!(velocities.len(), 2, "Should have 2 velocities"); - if let robocodec::CodecValue::Float64(v) = &velocities[0] { + if let robocodec::core::CodecValue::Float64(v) = &velocities[0] { assert!((*v - 0.1).abs() < f64::EPSILON, "velocity[0] should be 0.1"); } - if let robocodec::CodecValue::Float64(v) = &velocities[1] { + if let robocodec::core::CodecValue::Float64(v) = &velocities[1] { assert!((*v - 0.2).abs() < f64::EPSILON, "velocity[1] should be 0.2"); } } - if let Some(robocodec::CodecValue::Array(efforts)) = result.get("effort") { + if let Some(robocodec::core::CodecValue::Array(efforts)) = result.get("effort") { assert_eq!(efforts.len(), 2, "Should have 2 effort values"); for (i, e) in efforts.iter().enumerate() { - if let robocodec::CodecValue::Float64(v) = e { + if let robocodec::core::CodecValue::Float64(v) = e { assert_eq!(*v, 0.0, "effort[{}] should be 0.0", i); } } From 6c10dfd0a75058a6d8de84d15eebb7a17418cff6 Mon Sep 17 00:00:00 2001 From: Zhexuan Yang Date: Sun, 8 Feb 2026 00:34:13 +0800 Subject: [PATCH 2/5] docs: remove HTTP/HTTPS references and clean up unused dependencies - Remove HTTP/HTTPS sections from README files (support was removed) - Clarify that RoboRewriter only supports same-format rewriting - Remove bumpalo dependency (unused dead code) - Fix "Transformations" description to remove format conversion claims --- Cargo.lock | 1 - Cargo.toml | 1 - README.md | 39 ++++++-------------------- README_zh.md | 77 ++++++---------------------------------------------- 4 files changed, 17 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6071e45..d34b335 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2199,7 +2199,6 @@ dependencies = [ "async-trait", "aws-config", "aws-credential-types", - "bumpalo", "bytemuck", "byteorder", "bytes", diff --git a/Cargo.toml b/Cargo.toml index d1fb0b3..e1c74b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,6 @@ bzip2 = "0.4" crc32fast = "1.4" mcap = "0.24" rosbag = "0.6" -bumpalo = "3.16" bytemuck = "1.15" chrono = "0.4" tracing = "0.1" diff --git a/README.md b/README.md index ccde159..7c52a55 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - **Auto-Detection** - Format detected from file extension or URL scheme - **Fast** - Parallel processing with rayon, zero-copy memory-mapped files - **S3-Native** - First-class support for `s3://` URLs (AWS S3, MinIO, Alibaba OSS, etc.) -- **Transformations** - Topic/type renaming and format conversion built-in +- **Transformations** - Topic/type renaming during file rewriting ## Quick Start @@ -122,19 +122,6 @@ export AWS_SECRET_ACCESS_KEY="your-oss-secret-key" > **Note:** While we use AWS-standard environment variable names for compatibility, robocodec works with any S3-compatible storage service. -### Write to S3 - -```rust -use robocodec::RoboWriter; - -// Format detected from .mcap extension, S3 from s3:// URL -let mut writer = RoboWriter::create("s3://my-bucket/output.mcap")?; -let channel_id = writer.add_channel("/topic", "MessageType", "cdr", None)?; -// ... write messages ... -writer.finish()?; -} -``` - ### Custom S3 endpoints For S3-compatible services with custom endpoints: @@ -163,30 +150,20 @@ let reader = RoboReader::open( The rewriter processes files in the same format, optionally applying topic and type transformations: ```rust -use robocodec::RoboRewriter; - -let rewriter = RoboRewriter::open("input.mcap")?; -rewriter.rewrite("output.mcap")?; -``` - -**Note:** Cross-format conversion is not currently supported. Use the rewriter to transform data within the same format. - -### Rename topics during conversion - -```rust -use robocodec::{RoboRewriter, TransformBuilder}; +use robocodec::{RoboRewriter, TransformBuilder, RewriteOptions}; let transform = TransformBuilder::new() .with_topic_rename("/old/topic", "/new/topic") .build(); -let rewriter = RoboRewriter::with_options( - "input.mcap", - robocodec::RewriteOptions::default().with_transforms(transform) -)?; +let options = RewriteOptions::default().with_transforms(transform); +let rewriter = RoboRewriter::with_options("input.mcap", options)?; rewriter.rewrite("output.mcap")?; ``` +**Note:** The rewriter preserves the same format. Cross-format conversion is not currently supported. + + ## Installation ### Rust Users @@ -209,6 +186,8 @@ robocodec = { version = "0.1", features = ["jemalloc"] } | `s3` | S3-compatible storage support (AWS S3, MinIO, etc.) | ✅ Yes | | `python` | Python bindings | ❌ No | | `jemalloc` | Use jemalloc allocator (Linux only) | ❌ No | +| `python` | Python bindings | ❌ No | +| `jemalloc` | Use jemalloc allocator (Linux only) | ❌ No | ### Python Users diff --git a/README_zh.md b/README_zh.md index d6384c7..c88ca90 100644 --- a/README_zh.md +++ b/README_zh.md @@ -14,7 +14,7 @@ - **自动检测** - 从文件扩展名或 URL scheme 自动检测格式 - **高性能** - 使用 rayon 并行处理,零拷贝内存映射文件 - **原生 S3 支持** - 对 `s3://` URL 的一流支持(AWS S3、MinIO、阿里云 OSS 等) -- **数据转换** - 内置主题/类型重命名和格式转换功能 +- **数据转换** - 内置主题/类型重命名和文件重写功能 ## 快速开始 @@ -124,60 +124,6 @@ export AWS_SECRET_ACCESS_KEY="your-oss-secret-key" > **注意:** 虽然我们使用 AWS 标准的环境变量名称以确保兼容性,但 robocodec 可与任何兼容 S3 的存储服务配合使用。 -### 从 HTTP/HTTPS 读取 - -Robocodec 也支持直接从 HTTP/HTTPS URL 读取数据: - -```rust -use robocodec::RoboReader; - -// 格式从 URL 路径检测,通过 HTTP 访问 -let reader = RoboReader::open("https://example.com/data.mcap")?; -println!("找到 {} 个通道", reader.channels().len()); -``` - -> **注意:** HTTP 读取支持范围请求,可高效访问大文件。 - -#### HTTP 身份验证 - -对于需要身份验证的 HTTP 端点,robocodec 通过 `ReaderConfig` 支持 Bearer 令牌和基本身份验证: - -```rust -use robocodec::io::{RoboReader, ReaderConfig}; - -// Bearer 令牌(OAuth2/JWT) -let config = ReaderConfig::default().with_http_bearer_token("your-token-here"); -let reader = RoboReader::open_with_config("https://example.com/data.mcap", config)?; - -// 基本身份验证 -let config = ReaderConfig::default().with_http_basic_auth("username", "password"); -let reader = RoboReader::open_with_config("https://example.com/data.mcap", config)?; -``` - -或者,您可以通过 URL 查询参数提供身份验证: - -```rust -use robocodec::RoboReader; - -// 通过 URL 提供 Bearer 令牌 -let reader = RoboReader::open("https://example.com/data.mcap?bearer_token=your-token")?; - -// 通过 URL 提供基本身份验证(user:pass 编码) -let reader = RoboReader::open("https://example.com/data.mcap?basic_auth=user:pass")?; -``` - -### 写入到 S3 - -```rust -use robocodec::RoboWriter; - -// 格式从 .mcap 扩展名检测,S3 从 s3:// URL 检测 -let mut writer = RoboWriter::create("s3://my-bucket/output.mcap")?; -let channel_id = writer.add_channel("/topic", "MessageType", "cdr", None)?; -// ... 写入消息 ... -writer.finish()?; -``` - ### 自定义 S3 端点 对于具有自定义端点的兼容 S3 服务: @@ -201,31 +147,24 @@ let reader = RoboReader::open( )?; ``` -### 格式之间转换 +### 使用转换重写文件 -```rust -use robocodec::RoboRewriter; - -let rewriter = RoboRewriter::open("input.bag")?; -rewriter.rewrite("output.mcap")?; -``` - -### 转换时重命名主题 +重写器以相同格式处理文件,可选择应用主题和类型转换: ```rust -use robocodec::{RoboRewriter, TransformBuilder}; +use robocodec::{RoboRewriter, TransformBuilder, RewriteOptions}; let transform = TransformBuilder::new() .with_topic_rename("/old/topic", "/new/topic") .build(); -let rewriter = RoboRewriter::with_options( - "input.mcap", - robocodec::RewriteOptions::default().with_transforms(transform) -)?; +let options = RewriteOptions::default().with_transforms(transform); +let rewriter = RoboRewriter::with_options("input.mcap", options)?; rewriter.rewrite("output.mcap")?; ``` +**注意:** 重写器保持相同的格式。目前不支持跨格式转换。 + ## 安装 ### Rust 用户 From cb561ae93e9ed17aac2a9a86a06c31f9a82f9736 Mon Sep 17 00:00:00 2001 From: Zhexuan Yang Date: Sun, 8 Feb 2026 00:40:54 +0800 Subject: [PATCH 3/5] docs: remove outdated arena references from comments - Update MessageMetadata doc to remove "arena data" reference - Update parallel.rs module doc to remove "arena-based ownership model" - No arena allocation is used; CodecValue uses standard Rust heap allocation --- src/io/formats/mcap/parallel.rs | 2 +- src/io/metadata.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/formats/mcap/parallel.rs b/src/io/formats/mcap/parallel.rs index 6106fa3..04ebd05 100644 --- a/src/io/formats/mcap/parallel.rs +++ b/src/io/formats/mcap/parallel.rs @@ -5,7 +5,7 @@ //! Parallel MCAP reader with memory-mapped file access. //! //! This module provides MCAP-specific readers that implement the unified I/O traits -//! using the arena-based ownership model for safe lifetime management. +//! using memory-mapped files for efficient zero-copy access. //! //! **Note:** This implementation uses a custom MCAP parser with no external dependencies. //! It supports: diff --git a/src/io/metadata.rs b/src/io/metadata.rs index 2fc91d0..14547e1 100644 --- a/src/io/metadata.rs +++ b/src/io/metadata.rs @@ -285,7 +285,7 @@ impl TimestampedDecodedMessage { /// Metadata about a single message. /// -/// Lightweight version of `RawMessage` for references into arena data. +/// Lightweight version containing only message metadata without the data payload. #[derive(Debug, Clone, Copy, PartialEq)] pub struct MessageMetadata { /// Channel ID this message belongs to From 49f92a5eac5689ffb71c736f526cc9491ff49743 Mon Sep 17 00:00:00 2001 From: Zhexuan Yang Date: Sun, 8 Feb 2026 00:42:10 +0800 Subject: [PATCH 4/5] fix: update remaining CodecValue path in test file --- tests/cdr_cross_language_tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cdr_cross_language_tests.rs b/tests/cdr_cross_language_tests.rs index 40f23d5..6bac212 100644 --- a/tests/cdr_cross_language_tests.rs +++ b/tests/cdr_cross_language_tests.rs @@ -274,14 +274,14 @@ uint32 nanosec assert!(result.contains_key("names")); - if let Some(robocodec::CodecValue::Array(arr)) = result.get("names") { + if let Some(robocodec::core::CodecValue::Array(arr)) = result.get("names") { assert_eq!(arr.len(), 2); - if let robocodec::CodecValue::String(s1) = &arr[0] { + if let robocodec::core::CodecValue::String(s1) = &arr[0] { assert_eq!(s1, "left_arm_joint1"); } else { panic!("First element should be a string"); } - if let robocodec::CodecValue::String(s2) = &arr[1] { + if let robocodec::core::CodecValue::String(s2) = &arr[1] { assert_eq!(s2, "left_arm_joint2"); } else { panic!("Second element should be a string"); From 8192329c692056a78f77f800367febbf293e5ab5 Mon Sep 17 00:00:00 2001 From: Zhexuan Yang Date: Sun, 8 Feb 2026 00:49:13 +0800 Subject: [PATCH 5/5] fix: update doctest imports for API refactoring - Add FormatReader/FormatWriter trait imports to lib.rs doctests - Update Encoding imports to use robocodec::core::Encoding - Update DecodedMessage import to use robocodec::core::DecodedMessage --- src/core/mod.rs | 8 ++++---- src/encoding/cdr/encoder.rs | 2 +- src/encoding/codec.rs | 6 +++--- src/lib.rs | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/mod.rs b/src/core/mod.rs index 07f09fe..b6eba12 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -285,7 +285,7 @@ impl Encoding { /// # Example /// /// ``` - /// use robocodec::Encoding; + /// use robocodec::core::Encoding; /// /// assert!(Encoding::Cdr.is_cdr()); /// assert!(!Encoding::Protobuf.is_cdr()); @@ -300,7 +300,7 @@ impl Encoding { /// # Example /// /// ``` - /// use robocodec::Encoding; + /// use robocodec::core::Encoding; /// /// assert!(Encoding::Protobuf.is_protobuf()); /// assert!(!Encoding::Cdr.is_protobuf()); @@ -315,7 +315,7 @@ impl Encoding { /// # Example /// /// ``` - /// use robocodec::Encoding; + /// use robocodec::core::Encoding; /// /// assert!(Encoding::Json.is_json()); /// assert!(!Encoding::Cdr.is_json()); @@ -330,7 +330,7 @@ impl Encoding { /// # Example /// /// ``` - /// use robocodec::Encoding; + /// use robocodec::core::Encoding; /// /// assert_eq!(Encoding::Cdr.as_str(), "cdr"); /// assert_eq!(Encoding::Protobuf.as_str(), "protobuf"); diff --git a/src/encoding/cdr/encoder.rs b/src/encoding/cdr/encoder.rs index 7574d25..35678b0 100644 --- a/src/encoding/cdr/encoder.rs +++ b/src/encoding/cdr/encoder.rs @@ -460,7 +460,7 @@ impl CdrEncoder { /// # fn main() -> Result<(), Box> { /// use robocodec::encoding::cdr::encoder::CdrEncoder; /// use robocodec::schema::parse_schema; - /// use robocodec::DecodedMessage; + /// use robocodec::core::DecodedMessage; /// use robocodec::encoding::cdr::decoder::CdrDecoder; /// /// let schema = parse_schema("TestMsg", "int32 value\nfloat64 data")?; diff --git a/src/encoding/codec.rs b/src/encoding/codec.rs index 4277439..f95b0b4 100644 --- a/src/encoding/codec.rs +++ b/src/encoding/codec.rs @@ -19,7 +19,7 @@ //! //! ```no_run //! use robocodec::encoding::CodecFactory; -//! use robocodec::Encoding; +//! use robocodec::core::Encoding; //! //! # fn main() -> Result<(), Box> { //! let mut factory = CodecFactory::new(); @@ -91,7 +91,7 @@ impl CodecFactory { /// /// ``` /// use robocodec::encoding::CodecFactory; - /// use robocodec::Encoding; + /// use robocodec::core::Encoding; /// /// # fn main() -> Result<(), Box> { /// let factory = CodecFactory::new(); @@ -115,7 +115,7 @@ impl CodecFactory { /// /// ``` /// use robocodec::encoding::CodecFactory; - /// use robocodec::Encoding; + /// use robocodec::core::Encoding; /// /// # fn main() -> Result<(), Box> { /// let mut factory = CodecFactory::new(); diff --git a/src/lib.rs b/src/lib.rs index 2a54e68..780873b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ //! //! ```rust,no_run //! # fn main() -> Result<(), Box> { -//! use robocodec::RoboReader; +//! use robocodec::{RoboReader, FormatReader}; //! //! // Format auto-detected from extension //! let reader = RoboReader::open("file.mcap")?; @@ -54,7 +54,7 @@ //! //! ```rust,no_run //! # fn main() -> Result<(), Box> { -//! use robocodec::RoboWriter; +//! use robocodec::{RoboWriter, FormatWriter}; //! //! // Format detected from extension (.mcap, .bag, or .rrd) //! let mut writer = RoboWriter::create("output.mcap")?; @@ -71,7 +71,7 @@ //! //! ```rust,no_run //! # fn main() -> Result<(), Box> { -//! use robocodec::RoboReader; +//! use robocodec::{RoboReader, FormatReader}; //! //! // Read directly from S3 (requires `remote` feature, enabled by default) //! let reader = RoboReader::open("s3://my-bucket/path/to/data.mcap")?;