### Quick Start with nom-exif v3 Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p6-track-prelude.md Demonstrates how to use the one-shot helper functions for reading EXIF, track, and metadata from image and video files. Includes examples for single file types and auto-detection. ```rust use nom_exif::{read_exif, read_track, read_metadata, ExifTag, TrackInfoTag, Metadata}; // One image: let exif = read_exif("./testdata/exif.jpg")?; let make = exif.get(ExifTag::Make).and_then(|v| v.as_str()); // One video: let info = read_track("./testdata/meta.mov")?; let model = info.get(TrackInfoTag::Model).and_then(|v| v.as_str()); // Auto-detect: match read_metadata("./testdata/exif.jpg")? { Metadata::Exif(_) => { /* image */ } Metadata::Track(_) => { /* video/audio */ } } # Ok::<(), nom_exif::Error>(()) ``` -------------------------------- ### Install and Use rexiftool CLI Source: https://github.com/mindeng/nom-exif/blob/main/README.md Install the `rexiftool` CLI using cargo. Examples show basic usage, JSON output, and batch processing of directories. ```sh cargo install rexiftool ``` ```sh rexiftool photo.heic # key => value ``` ```sh rexiftool photo.heic -j # JSON ``` ```sh rexiftool ./photos/ # batch (non-recursive) ``` -------------------------------- ### Build Cargo Examples Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p5-exif-iter.md Builds all cargo examples with all features enabled. ```bash cargo build --all-features --examples ``` -------------------------------- ### Quick Start: Batch Processing Media Files Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p6-track-prelude.md For batch processing, initialize MediaParser once and reuse its buffer. This example demonstrates parsing Exif for images and track data for video/audio files. Ensure MediaSource can open the specified paths. ```rust use nom_exif::{MediaKind, MediaParser, MediaSource}; let mut parser = MediaParser::new(); for path in ["./testdata/exif.jpg", "./testdata/meta.mov"] { let ms = MediaSource::open(path)?; match ms.kind() { MediaKind::Image => { let _ = parser.parse_exif(ms)? \ ; } // Corrected from parse_exif MediaKind::Track => { let _ = parser.parse_track(ms)? \ ; } // Corrected from parse_track } } # Ok::<(), nom_exif::Error>(()) ``` -------------------------------- ### Add Quick Start Subsection for In-Memory Bytes Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p7-memory-source.md This snippet adds a new subsection titled "Reading from in-memory bytes" to the Quick Start guide in `src/lib.rs`. It explains how to use `*_from_bytes` helpers for zero-copy parsing of in-memory data and provides a concrete example using `read_exif_from_bytes`. ```rust //! # Reading from in-memory bytes //! //! When the payload is already in RAM (WASM, mobile, HTTP proxy, decoded //! response body), use the `*_from_bytes` helpers to skip the `File` / //! `Read` round-trip entirely. Memory mode is **zero-copy**: the underlying //! allocation is shared with the returned [`Exif`] / [`ExifIter`] / //! [`TrackInfo`] via [`bytes::Bytes`] reference counting. //! //! ```rust //! use nom_exif::{read_exif_from_bytes, ExifTag}; //! //! let raw = std::fs::read("./testdata/exif.jpg")?; //! let exif = read_exif_from_bytes(raw)?; //! assert_eq!(exif.get(ExifTag::Make).and_then(|v| v.as_str()), Some("vivo")); //! # Ok::<(), nom_exif::Error>(()) //! ``` //! //! For batch processing of many in-memory payloads, build a [`MediaParser`] //! once and call [`MediaParser::parse_exif_bytes`] / //! [`MediaParser::parse_track_bytes`] per payload. ``` -------------------------------- ### Commit Changes Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p6-track-prelude.md Example of how to stage and commit changes to the migration guide test file. ```bash git add tests/migration_guide.rs git commit -m "test(migration): runnable §5 migration guide as integration test" ``` -------------------------------- ### Smoke test examples with all features Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p3-parser-entry.md Builds all example binaries with all features enabled. This verifies that the examples compile correctly with the latest changes. ```bash cargo build --examples --all-features 2>&1 | tail -3 ``` -------------------------------- ### Run Documentation Tests Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p6-docs.md Verify that all documentation examples and tests pass when compiled. This ensures that the code examples in the documentation are accurate and functional. ```bash cargo test --all-features --doc ``` -------------------------------- ### Replace README Batch-Processing Example with `from_memory` Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p0-source-unification.md This snippet updates the batch-processing example in the README to use `MediaSource::from_memory`. It's a simplified version for parsing EXIF data from memory. ```rust use nom_exif::{MediaParser, MediaSource}; let mut parser = MediaParser::new(); let raw = std::fs::read("./testdata/exif.jpg")?; let ms = MediaSource::from_memory(raw)?; let iter = parser.parse_exif(ms)?; # let _ = iter; Ok::<(), nom_exif::Error>(()) ``` -------------------------------- ### Command to Build with All Features Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p2-internal-async.md This bash command builds the project with all features enabled, including examples, and shows the last few lines of the output, useful for checking build success. ```bash cargo build --all-features --examples 2>&1 | tail -3 ``` -------------------------------- ### Verify Baseline Build and Tests Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p1-errors.md Confirm that the project builds and tests pass with all features enabled. This is a prerequisite for starting the migration. ```bash cargo build --all-features 2>&1 | tail -5 ``` ```bash cargo test --all-features --no-run 2>&1 | tail -5 ``` -------------------------------- ### Update Example `rexiftool.rs` Iterator Usage Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p5-exif-iter.md Modify the `rexiftool.rs` example to correctly handle the `ExifIter` output using the new `ExifIterEntry` API, specifically `take_result()` and updated tag/tag_code access. ```rust let iter: ExifIter = parser.parse_exif(ms).inspect_err(handle_parsing_error)?; iter.into_iter() .filter_map(|mut x| { let res = x.take_result(); match res { Ok(v) => Some(( x.tag() .map(|x| x.to_string()) .unwrap_or_else(|| format!("Unknown(0x{{:04x}})", x.tag_code())), v, )), Err(e) => { tracing::warn!(?e); None } } }) .collect::>() ``` -------------------------------- ### Check Baseline Coverage for webm.rs Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-12-test-coverage-spot-fix.md Run this command to get the initial line coverage report for `src/ebml/webm.rs` before making any changes. ```bash cargo llvm-cov --package nom-exif --all-features --summary-only 2>&1 | grep 'webm.rs' ``` -------------------------------- ### Install Rexiftool with Cargo Source: https://github.com/mindeng/nom-exif/blob/main/crates/rexiftool/README.md Install the rexiftool CLI using the Cargo package manager. This is the primary method for obtaining the tool on systems with Rust and Cargo installed. ```sh cargo install rexiftool ``` -------------------------------- ### Runnable Migration Guide Doc-Tests Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p6-track-prelude.md This Rust code defines integration tests for the v3 API migration guide. It lives in `tests/` to compile as a downstream crate would, validating the public API surface end-to-end. Update the corresponding row in V3_API_DESIGN.md and CHANGELOG.md if these tests break. ```rust //! Runnable migration guide. Each test exercises the v3 side of one //! migration row in `docs/V3_API_DESIGN.md` §5. Lives in `tests/` so it //! compiles as a downstream crate would, validating the public API //! surface end-to-end. //! //! If you change the public surface and one of these breaks, **update //! the corresponding row in V3_API_DESIGN.md §5 and CHANGELOG.md** too — //! the three artifacts are meant to stay in lock-step. use nom_exif::*; // ─── §5.1 entry & parsing ────────────────────────────────────────────────── #[test] fn s5_1_media_source_open() { let ms = MediaSource::open("./testdata/exif.jpg").unwrap(); assert_eq!(ms.kind(), MediaKind::Image); } #[test] fn s5_1_top_level_read_exif() { let exif = read_exif("./testdata/exif.jpg").unwrap(); assert!(exif.get(ExifTag::Make).is_some()); } #[test] fn s5_1_parser_parse_exif() { let mut parser = MediaParser::new(); let ms = MediaSource::open("./testdata/exif.jpg").unwrap(); let _iter = parser.parse_exif(ms).unwrap(); } #[test] fn s5_1_parser_parse_track() { let mut parser = MediaParser::new(); let ms = MediaSource::open("./testdata/meta.mov").unwrap(); let _info: TrackInfo = parser.parse_track(ms).unwrap(); } // ─── §5.2 errors ─────────────────────────────────────────────────────────── #[test] fn s5_2_malformed_variant_pattern() { // Confirms the structured-error pattern compiles. The `_` arm // proves Error is exhaustive over the public variants we expose. fn _classify(err: Error) -> &'static str { match err { Error::Malformed { .. } => "malformed", Error::UnexpectedEof => "eof", Error::UnsupportedFormat => "unsupported", Error::Io(_) => "io", Error::ExifNotFound => "no_exif", Error::TrackNotFound => "no_track", _ => "other", // forwards-compatible for future variants } } } #[test] fn s5_2_malformed_kind_imports_from_top_level() { let _kind: MalformedKind = MalformedKind::Exif; } // ─── §5.3 EntryValue accessors ───────────────────────────────────────────── #[test] fn s5_3_as_datetime_replaces_as_time_components() { let exif = read_exif("./testdata/exif.jpg").unwrap(); let dto = exif.get(ExifTag::DateTimeOriginal).unwrap(); let _: Option = dto.as_datetime(); } #[test] fn s5_3_as_u8_slice_replaces_as_u8array() { let exif = read_exif("./testdata/exif.jpg").unwrap(); if let Some(v) = exif.get(ExifTag::MakerNote) { let _: Option<&[u8]> = v.as_u8_slice(); } } // ─── §5.4 ExifTag ────────────────────────────────────────────────────────── #[test] fn s5_4_exif_tag_from_code() { assert_eq!(ExifTag::from_code(0x010f), Some(ExifTag::Make)); assert!(ExifTag::from_code(0xffff).is_none()); } #[test] fn s5_4_exif_tag_name_and_from_str() { use std::str::FromStr; assert_eq!(ExifTag::Make.name(), "Make"); assert_eq!(ExifTag::Make.to_string(), "Make"); assert_eq!(ExifTag::from_str("Make").unwrap(), ExifTag::Make); let err = ExifTag::from_str("Bogus").unwrap_err(); assert!(matches!(err, ConvertError::UnknownTagName(_))); } // ─── §5.5 Exif / ExifIter ────────────────────────────────────────────────── #[test] fn s5_5_exif_gps_info() { let exif = read_exif("./testdata/exif.heic").unwrap(); let _: Option<&GPSInfo> = exif.gps_info(); } #[test] fn s5_5_exif_get_by_code_and_get_in() { let exif = read_exif("./testdata/exif.jpg").unwrap(); let _ = exif.get_by_code(IfdIndex::MAIN, 0x0110); let _ = exif.get_in(IfdIndex::MAIN, ExifTag::Model); } #[test] fn s5_5_exif_iter_yields_eager_entries() { let exif = read_exif("./testdata/exif.jpg").unwrap(); let n = exif.iter().filter(|e| e.ifd == IfdIndex::MAIN).count(); assert!(n > 0); } #[test] fn s5_5_exif_errors_accessor() { let exif = read_exif("./testdata/exif.jpg").unwrap(); let _: &[(IfdIndex, TagOrCode, EntryError)] = exif.errors(); } #[test] fn s5_5_exif_iter_entry_into_result() { let mut parser = MediaParser::new(); let ms = MediaSource::open("./testdata/exif.jpg").unwrap(); for entry in parser.parse_exif(ms).unwrap() { ``` -------------------------------- ### Verify Clean Git Tree Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p1-errors.md Before starting, ensure the working directory is clean on the 'v3' branch. ```bash cd /Users/min/dev/nom-exif && git status --short ``` -------------------------------- ### Quick Start: Read Image, Video, or Auto-detect Metadata Source: https://github.com/mindeng/nom-exif/blob/main/README.md Demonstrates how to read EXIF data from images, track information from videos, or automatically detect the media type and parse its metadata using convenience functions. Requires importing relevant tags and metadata types. ```rust use nom_exif::{read_exif, read_track, read_metadata, ExifTag, TrackInfoTag, Metadata}; // One image: let exif = read_exif("./testdata/exif.jpg")?; let make = exif.get(ExifTag::Make).and_then(|v| v.as_str()); // One video: let info = read_track("./testdata/meta.mov")?; let model = info.get(TrackInfoTag::Model).and_then(|v| v.as_str()); // Auto-detect: match read_metadata("./testdata/exif.jpg")? { Metadata::Exif(_) => { /* image */ } Metadata::Track(_) => { /* video/audio */ } } # Ok::<(), nom_exif::Error>(()) ``` -------------------------------- ### Smoke Test PNG Fixtures with rexiftool Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p6-docs.md Iterate through various PNG fixture files and run the `rexiftool` example on each. This verifies that the tool can process different types of PNGs, including those with EXIF, legacy EXIF, APP1, both, and text-only metadata. ```bash for f in testdata/exif.png testdata/exif-legacy.png testdata/exif-legacy-app1.png testdata/exif-both.png testdata/text-only.png; do echo "=== $f ===" cargo run --quiet --example rexiftool --features serde -- "$f" || echo "(failed: $f)" done ``` -------------------------------- ### Run Cargo Test Command Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p1-errors.md Execute this command in the terminal to run the tests and confirm that the initial setup fails as expected before implementing the changes. ```bash cargo test --lib entry_error_tests 2>&1 | tail -10 ``` -------------------------------- ### Implement `parse_exif_bytes` for MediaParser Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p7-memory-source.md This method serves as the entry point for memory-mode EXIF parsing. It takes a `MediaSource<()>` containing the byte payload, installs it into the parser state, and uses a placeholder reader for zero-copy operations. It returns `Error::ExifNotFound` if the payload is not an image type. ```rust /// Parse Exif metadata from an in-memory byte payload built via /// [`MediaSource::<()>::from_bytes`]. Returns `Error::ExifNotFound` if the /// payload is a `Track` (use [`Self::parse_track_bytes`] instead). /// /// Memory-mode parsing is **zero-copy**: the underlying `Bytes` is shared /// with the returned [`ExifIter`] (and its sub-IFDs / CR3 CMT blocks) via /// reference counting. No `Vec` is allocated for the parse buffer. pub fn parse_exif_bytes(&mut self, mut ms: MediaSource<()>) -> crate::Result { self.reset(); let memory = ms .memory .take() .expect("MediaSource<()> must have memory (only constructor is from_bytes)"); self.state.set_memory(memory); let res: crate::Result = (|| { if !matches!(ms.mime, crate::file::MediaMime::Image(_)) { return Err(crate::Error::ExifNotFound); } // Placeholder reader: never read from in memory mode (fill_buf // short-circuits; clear_and_skip uses AdvanceOnly). let mut empty = std::io::empty(); crate::exif::parse_exif_iter( self, ms.mime.unwrap_image(), &mut empty, // Placeholder skip-by-seek: never invoked. |_, _| Ok(false), ) })(); self.reset(); res } ``` -------------------------------- ### Run Full Test Suite and Search for Stragglers Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p5-exif-iter.md Run the complete test suite and then filter the output to check for any unexpected test results. Also, search the source code and examples for specific terms related to ExifIter parsing and data retrieval. ```bash cargo test --all-features 2>&1 | grep "test result" ``` ```bash grep -rn "parse_gps_info\|clone_and_rewind\|take_value\|take_result\|ParsedExifEntry\|get_by_ifd_tag_code\|get_gps_info\|ExifTagCode" src/ examples/ ``` -------------------------------- ### Migrate examples/rexiftool.rs to v3 Parser Surface Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p3-parser-entry.md This snippet demonstrates the migration of `examples/rexiftool.rs` to use the v3 parser entry surface. It replaces `MediaSource::file_path` with `MediaSource::open` and `parser.parse` with `parser.parse_exif` or `parser.parse_track` based on the media kind. Ensure `MediaKind` is imported. ```rust // L104 let ms = MediaSource::open(path).inspect_err(handle_parsing_error)?; // L105-129 let values = match ms.kind() { MediaKind::Image => { let iter: ExifIter = parser.parse_exif(ms).inspect_err(handle_parsing_error)?; iter.into_iter() .filter_map(/* unchanged body */) .collect::>() } MediaKind::Track => { let info: TrackInfo = parser.parse_track(ms)?; info.into_iter() .map(|x| (x.0.to_string(), x.1)) .collect::>() } }; ``` -------------------------------- ### Build and test project Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-12-test-coverage-spot-fix.md Compile the project with all features and run the library tests to ensure no regressions were introduced. ```bash cargo build --all-features --package nom-exif ``` ```bash cargo test --all-features --package nom-exif --lib ``` -------------------------------- ### MediaSource Kind Method Source: https://github.com/mindeng/nom-exif/blob/main/docs/V3_API_DESIGN.md Method to get the MediaKind of the MediaSource. ```rust impl MediaSource { pub fn kind(&self) -> MediaKind; } ``` -------------------------------- ### Implement MediaSource::open and MediaSource::from_file Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p3-parser-entry.md Implements the `open` and `from_file` methods for `MediaSource`, providing v3-preferred entry points for opening files by path or using an existing file handle. Includes forwarders for v2-shape constructors. ```rust impl MediaSource { /// Open a file at `path` and parse its header to detect the media format. /// /// This is the v3-preferred entry point for the common case of "I have a /// path on disk". For an already-open file handle use [`Self::from_file`]; /// for a generic `Read + Seek` source use [`Self::seekable`]. pub fn open>(path: P) -> crate::Result { Self::seekable(File::open(path)?) } /// Wrap an already-open `File` and parse its header. pub fn from_file(file: File) -> crate::Result { Self::seekable(file) } // v2-shape constructors (deleted at end of P3; tests still use them). pub fn file_path>(path: P) -> crate::Result { Self::open(path) } pub fn file(file: File) -> crate::Result { Self::from_file(file) } } ``` -------------------------------- ### Build Documentation Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p1-errors.md Generate documentation for the project, excluding dependencies, to check for any new warnings introduced by the P1 changes. Captures the last 10 lines of output. ```bash cargo doc --no-deps --all-features 2>&1 | tail -10 ``` -------------------------------- ### MediaSource Implementation (File Open) Source: https://github.com/mindeng/nom-exif/blob/main/docs/V3_API_DESIGN.md Implementation of MediaSource for opening a file from a path, returning a Result. ```rust impl MediaSource { pub fn open(path: impl AsRef) -> Result; } ``` -------------------------------- ### Commit Changes Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p4-values.md Example commit message for the changes related to ExifDateTime, as_datetime, and EntryValue variant renaming. ```bash git add src/values.rs src/mov.rs src/lib.rs git commit -m "feat(values)!: ExifDateTime + as_datetime; drop as_time_components Per spec §3.6: replace the awkward (NaiveDateTime, Option) tuple with an ExifDateTime enum (Aware|Naive) plus three accessors — aware() / into_naive() / or_offset(fallback). Rename EntryValue::Time variant to EntryValue::DateTime so the variant name matches its payload. Delete the panic-prone From> and the redundant From<&String>." ``` -------------------------------- ### Check Baseline Test Coverage Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-12-test-coverage-spot-fix.md Run this command to get the current line coverage for `src/values.rs` before making any changes. ```bash cargo llvm-cov --package nom-exif --all-features --summary-only 2>&1 | grep 'values.rs' ``` -------------------------------- ### Run full test suite Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-12-test-coverage-spot-fix.md Execute all tests for the project with all features enabled to ensure overall stability. ```bash cargo test --all-features --package nom-exif ``` -------------------------------- ### ExifDateTime Usage Example Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p4-values.md Demonstrates the usage of the ExifDateTime enum with `as_datetime` and `or_offset` methods. Requires chrono crate imports. ```rust assert_eq!(edt.into_naive(), ndt); assert_eq!(edt.or_offset(FixedOffset::east_opt(0).unwrap()), dt); let ev = EntryValue::NaiveDateTime(ndt); let edt = ev.as_datetime().unwrap(); assert_eq!(edt.aware(), None); assert_eq!(edt.into_naive(), ndt); assert_eq!(edt.or_offset(offset), dt); } ``` -------------------------------- ### Verify Build and Run Tests Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p1-errors.md Compile the project with all features enabled and run all tests within the `error::tests` module to ensure the build succeeds and all tests pass. ```bash cargo build --all-features 2>&1 | tail -3 ``` ```bash cargo test --lib error::tests 2>&1 | tail -10 ``` -------------------------------- ### Commit TrackInfo::has_embedded_media Changes Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p6-track-prelude.md Example Git commit command to stage and commit the changes related to the `TrackInfo::has_embedded_media` implementation. ```bash git add src/video.rs git commit -m "feat(track): TrackInfo::has_embedded_media (always false in v3.0.0)" ``` -------------------------------- ### Generate Documentation and Run Clippy Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p2-internal-async.md Generates documentation without dependencies and runs clippy with warnings disabled to ensure no new warnings are introduced. Checks the last 3 lines for doc output and last 10 for clippy. ```bash cargo doc --no-deps --all-features 2>&1 | tail -3 ``` ```bash cargo clippy --all-features -- -D warnings 2>&1 | tail -10 ``` -------------------------------- ### ExifTag Name Conversion Source: https://github.com/mindeng/nom-exif/blob/main/docs/MIGRATION.md To get the string name of an `ExifTag`, use the `.name()` method or `.to_string()`. The `From<&str> for ExifTag` implementation has been removed. ```rust t.name() // or t.to_string() ``` -------------------------------- ### Run Master Verification Chain Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p3-parser-entry.md Execute these commands to ensure the project builds correctly with and without default features, and that documentation generation is clean. All commands should complete successfully with no warnings. ```bash cargo build --no-default-features 2>&1 | tail -3 ``` ```bash cargo build --all-features 2>&1 | tail -3 ``` ```bash cargo build --examples --all-features 2>&1 | tail -3 ``` ```bash cargo test --lib --no-default-features 2>&1 | tail -3 ``` ```bash cargo test --lib --all-features 2>&1 | tail -3 ``` ```bash cargo test --doc --all-features 2>&1 | tail -3 ``` ```bash cargo doc --no-deps --all-features 2>&1 | tail -10 ``` -------------------------------- ### Exif::get_in Method Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p5-exif-iter.md Introduces the `get_in` method for retrieving EXIF entry values by IFD index and tag, with enhanced documentation and examples. ```APIDOC ## Exif::get_in ### Description Retrieves the entry value for a specified `tag` within a given `ifd`. This method is designed to handle cases where the tag might not be parsed successfully or may not exist, returning `None` in such scenarios. For raw tag codes, `Self::get_by_code` should be used. ### Method ```rust pub fn get_in(&self, ifd: IfdIndex, tag: ExifTag) -> Option<&EntryValue> ``` ### Endpoint N/A (Rust method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```rust use nom_exif::*; fn main() -> Result<()> { let mut parser = MediaParser::new(); let ms = MediaSource::open("./testdata/exif.jpg")?; let iter = parser.parse_exif(ms)?; let exif: Exif = iter.into(); assert_eq!(exif.get_in(IfdIndex::MAIN, ExifTag::Model).unwrap(), &"vivo X90 Pro+".into()); Ok(()) } ``` ### Response #### Success Response - `Option<&EntryValue>`: Returns a reference to the `EntryValue` if the tag exists in the specified IFD, otherwise `None`. #### Response Example None ### Error Handling - Parsing errors related to the tag are not reported directly. Use `Self::errors` to inspect per-entry errors. ``` -------------------------------- ### Mark MediaSource::from_bytes as Deprecated Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p0-source-unification.md Add the `#[deprecated]` attribute to `MediaSource::<()>::from_bytes` to indicate its upcoming removal and guide users to newer methods. ```rust #[deprecated( since = "3.3.0", note = "Use `MediaSource::from_memory` and the unified `parse_*` \ methods (which now accept memory-mode sources directly). \ The `MediaSource<()>` shape will be removed in v4." )] pub fn from_bytes(bytes: impl Into) -> crate::Result { // ... existing body unchanged ... } ``` -------------------------------- ### Synchronous MediaSource Open and From File Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p3-parser-entry.md Implements `MediaSource::open` to open a file by path and `MediaSource::from_file` to wrap an existing file handle. These methods are the preferred entry points for synchronous media processing. ```APIDOC ## MediaSource::open and MediaSource::from_file ### Description Provides synchronous methods to open media files either by path or by using an existing file handle. ### Methods #### `MediaSource::open>(path: P) -> crate::Result` Opens a file at the specified `path` and parses its header to detect the media format. This is the v3-preferred entry point for the common case of having a path on disk. #### `MediaSource::from_file(file: File) -> crate::Result` Wraps an already-open `File` handle and parses its header to detect the media format. ### Example Usage ```rust use std::fs::File; // Using open(path) let ms_open = MediaSource::open("testdata/exif.jpg").unwrap(); assert_eq!(ms_open.kind(), MediaKind::Image); // Using from_file(file) let f = File::open("testdata/exif.jpg").unwrap(); let ms_from_file = MediaSource::from_file(f).unwrap(); assert_eq!(ms_from_file.kind(), MediaKind::Image); ``` ``` -------------------------------- ### Run Full Test Suite Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p1-errors.md Execute the complete test suite with and without default features to ensure all functionalities are working as expected. Captures the last 10 lines of output for review. ```bash cargo test --all-features 2>&1 | tail -10 ``` ```bash cargo test --no-default-features 2>&1 | tail -10 ``` -------------------------------- ### Verify build and test with all features Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p3-parser-entry.md Builds the project and runs tests with all features enabled. This is expected to produce errors in the async path before Task 7 is completed, indicating the need for further refactoring. ```bash cargo build --all-features 2>&1 | tail -20 ``` ```bash cargo test --lib --all-features 2>&1 | tail -3 ``` -------------------------------- ### Refactor parse_track_from_bytes for Deprecation Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p0-source-unification.md Refactor `parse_track_from_bytes` to delegate to `self.parse_track` via `ms.into_empty()`. This method is deprecated, guiding users to the unified `parse_track` with `MediaSource::from_memory`. ```rust /// **Deprecated since v3.3.0**: use [`Self::parse_track`] with /// [`MediaSource::from_memory`] directly. #[deprecated( since = "3.3.0", note = "Use `parse_track` with `MediaSource::from_memory`." )] pub fn parse_track_from_bytes(&mut self, ms: MediaSource<()>) -> crate::Result { self.parse_track(ms.into_empty()) } ``` -------------------------------- ### Build and Test All Features Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p2-internal-async.md Execute a full build and run all library tests with all features enabled. This command is useful for verifying the integrity of the project after refactoring. ```bash cargo build --all-features 2>&1 | tail -3 ``` ```bash cargo test --lib --all-features 2>&1 | tail -3 ``` -------------------------------- ### Commit PngTextChunks Changes Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p3-public-types.md Example Git commit message for adding the PngTextChunks public type. It summarizes the functionality and its role in handling PNG text metadata. ```bash git add src/exif/png_text.rs src/exif.rs git commit -m "$(cat <<'EOF' feat: add PngTextChunks public type Opaque wrapper around Vec<(String, String)> exposing get / get_all / iter / len / is_empty. Latin-1 decoded key/value pairs in PNG file order, duplicates preserved. Will be the payload of ImageFormatMetadata::Png in the next commit. EOF )" ``` -------------------------------- ### Add Deprecation Note to README Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p0-source-unification.md This markdown snippet is added to the README to inform users about the deprecation of `*_from_bytes` helpers since v3.3.0 and directs them to the migration guide. ```markdown **Migration note (v3.3+)**: `MediaSource::<()>::from_bytes`, `read_exif_from_bytes`, and the other `*_from_bytes` helpers are deprecated since v3.3.0 and will be removed in v4. Replace with `MediaSource::from_memory` + `parse_exif` / `read_exif`. See [`docs/MIGRATION.md`](docs/MIGRATION.md). ``` -------------------------------- ### Run Full Test Suite (Sync and Async) Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p2-internal-async.md Executes the full test suite with and without default features to ensure all tests pass. Checks the last 3 lines of the output for success indicators. ```bash cargo test --all-features 2>&1 | tail -3 ``` ```bash cargo test --no-default-features 2>&1 | tail -3 ``` -------------------------------- ### Test ExifIter Clone Rewound Functionality Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p5-exif-iter.md Ensures that `clone_rewound` creates a new ExifIter instance that starts from the beginning, independent of the original iterator's consumed state. ```rust #[test] fn exif_iter_clone_rewound_yields_independent_full_iter() { use crate::{MediaParser, MediaSource}; let mut parser = MediaParser::new(); let ms = MediaSource::open("testdata/exif.jpg").unwrap(); let mut iter = parser.parse_exif(ms).unwrap(); let _consumed = iter.by_ref().take(2).count(); let cloned = iter.clone_rewound(); // cloned starts from entry 0 even though `iter` consumed 2 entries. let cloned_total = cloned.count(); let remaining = iter.count(); assert!(cloned_total > remaining); } ``` -------------------------------- ### Build and Test Commands Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p2-internal-async.md Commands to build all features and run tests, with output redirection to capture the last few lines. ```bash cargo build --all-features 2>&1 | tail -3 ``` ```bash cargo test --lib --all-features 2>&1 | tail -10 ``` -------------------------------- ### Example: Commit Message for Changelog Update Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p6-docs.md This bash script generates a commit message summarizing the v3.3 release notes, including added features, deprecations, and future plans. ```bash git add CHANGELOG.md git commit -m "$(cat <<'EOF' docs(changelog): v3.3 — PNG support + source-input unification Three sections under Unreleased: - Added: PNG support, parse_image_metadata, from_memory, new public types, rexiftool format-metadata printing - Deprecated: from_bytes / parse_*_from_bytes / read_*_from_bytes family (removed in v4) - Notes: read_image_metadata top-level helpers deferred to v4; iTXt/zTXt deferred (non-breaking when added) EOF )" ``` -------------------------------- ### Build with new features Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p3-parser-entry.md Build the project with the newly renamed features 'tokio' and 'serde' enabled, excluding default features. This verifies the feature renaming was successful and the build process is clean. ```bash cargo build --no-default-features --features tokio,serde 2>&1 | tail -20 ``` -------------------------------- ### Migrate Doctest to from_memory Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p0-source-unification.md Replaces the deprecated `read_exif_from_bytes` doctest with an example using `MediaSource::from_memory` and `MediaParser::parse_exif`. This demonstrates the modern API for reading EXIF data from in-memory bytes. ```rust //! ```rust //! use nom_exif::{MediaSource, MediaParser, ExifTag}; //! //! let raw = std::fs::read("./testdata/exif.jpg")?; //! let ms = MediaSource::from_memory(raw)?; //! let mut parser = MediaParser::new(); //! let iter = parser.parse_exif(ms)?; //! let exif: nom_exif::Exif = iter.into(); //! assert_eq!(exif.get(ExifTag::Make).and_then(|v| v.as_str()), Some("vivo")); //! # Ok::<(), nom_exif::Error>(()) //! ``` ``` -------------------------------- ### Run Full Test Suite Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p6-docs.md Execute all tests with all features enabled to ensure comprehensive coverage. Verify that the test count has increased as planned. ```bash cargo test --all-features ``` -------------------------------- ### Commit Message for Legacy EXIF Tests Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p5-legacy-exif.md Example commit message detailing the addition of integration tests for legacy EXIF data in PNG files, including the creation of new fixtures. ```bash git add tests/png_fixtures.rs tests/png.rs testdata/exif-legacy.png testdata/exif-legacy-app1.png testdata/exif-both.png git commit -m "$(cat <<'EOF' test: integration tests for PNG legacy hex-encoded EXIF Three new fixtures: EOF )" ``` -------------------------------- ### Test New Parser Has No Upfront Allocation Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p4_5-bytes.md Checks that a newly created `MediaParser` does not perform any upfront memory allocation. This confirms that the parser starts with an empty state regarding its internal buffer. ```rust #[test] fn parser_new_does_no_upfront_allocation() { let parser = MediaParser::new(); assert!(parser.state.cached_ptr_for_test().is_none()); assert!(parser.state.buf_is_none_for_test()); } ``` -------------------------------- ### Check Git Tag for v3-p1-done Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p2-internal-async.md Verifies that the current Git HEAD is at the `v3-p1-done` tag. If not, it shows the commit history since that tag. This is a pre-flight check to ensure the correct starting point. ```bash git describe --tags --exact-match HEAD 2>/dev/null || git log --oneline HEAD..v3-p1-done ``` -------------------------------- ### Implement MediaParser::parse_exif and MediaParser::parse_track Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p3-parser-entry.md These methods are implemented by delegating to the existing generic `parse` method. This approach provides dedicated entry points for Exif and track parsing while maintaining backward compatibility until `parse` is removed. ```rust /// Parse Exif metadata from an image source. Returns `Error::ExifNotFound` /// if the source is a `Track` (use [`Self::parse_track`] instead). pub fn parse_exif(&mut self, ms: MediaSource) -> crate::Result { self.parse(ms) } /// Parse track info from a video/audio source. Returns `Error::TrackNotFound` /// if the source is an `Image` (use [`Self::parse_exif`] instead). pub fn parse_track(&mut self, ms: MediaSource) -> crate::Result { self.parse(ms) } ``` -------------------------------- ### Stage and Commit EXIF Changes Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p5-exif-iter.md Stages specific Rust source files and the `rexiftool.rs` example for commit, then commits with a descriptive message detailing the renaming of `ParsedExifEntry` to `ExifIterEntry` and associated API changes. ```bash git add src/exif/exif_iter.rs src/exif/exif_exif.rs src/exif.rs src/lib.rs examples/rexiftool.rs git commit -m "feat(exif): rename ParsedExifEntry -> ExifIterEntry; private fields Spec §3.5: drop the panic-on-second-call take_value/take_result API; yield ExifIterEntry with private fields and a value-xor-error invariant. Borrow access via result()/value()/error(); ownership transfer via into_result(self). Examples and From for Exif migrated." ``` -------------------------------- ### Generate Documentation Without Dependencies Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-08-v3-p4-values.md Generate documentation for the project, excluding dependencies, with all features enabled. Ensure there are no warnings. ```bash cargo doc --no-deps --all-features ``` -------------------------------- ### Commit Message for README Migration Source: https://github.com/mindeng/nom-exif/blob/main/docs/superpowers/plans/2026-05-10-png-p0-source-unification.md This bash snippet represents the commit message used after migrating the README examples. It summarizes the changes made, including the API update and the addition of a migration note. ```bash git add README.md git commit -m "$(cat <<'EOF' docs: migrate README in-memory examples to from_memory The In-Memory Bytes section now demonstrates the v3.3 unified API. Adds a Migration Note paragraph linking to docs/MIGRATION.md for existing v3.0-3.2 users on the deprecated *_from_bytes path. EOF )" ```