JSON-Rohbericht maschinenlesbar
{
"data": {
"repo": {
"topics": [
"exif",
"heif",
"jpeg",
"metadata",
"mp4",
"nom",
"parser",
"quicktime",
"rust",
"matroska",
"mkv",
"webm",
"mov",
"raw",
"tiff",
"cr3"
],
"is_fork": false,
"size_kb": 29166,
"has_wiki": true,
"homepage": "https://crates.io/crates/nom-exif",
"languages": {
"Rust": 598842,
"Shell": 696
},
"pushed_at": "2026-07-20T03:26:22Z",
"created_at": "2024-02-15T04:05:16Z",
"owner_type": "User",
"updated_at": "2026-07-07T07:20:46Z",
"description": "Exif/metadata parsing library written in pure Rust, both image (JPEG, PNG, HEIC/HEIF, AVIF, TIFF, Phase One IIQ, Fujifilm RAF, Canon CR3) and video/audio (MP4, MOV, 3GP, MKV, WEBM, MKA) files are supported.",
"is_archived": false,
"is_disabled": false,
"license_spdx": "MIT",
"default_branch": "main",
"license_spdx_raw": "MIT",
"primary_language": "Rust",
"significant_languages": [
"Rust"
]
},
"owner": {
"blog": "https://mincodes.com",
"name": "Min Deng",
"type": "User",
"login": "mindeng",
"company": null,
"location": "Shenzhen",
"followers": 39,
"avatar_url": "https://avatars.githubusercontent.com/u/921932?v=4",
"created_at": "2011-07-18T03:16:28Z",
"is_verified": null,
"public_repos": 70,
"account_age_days": 5482
},
"license": {
"state": "standard",
"spdx_id": "MIT",
"raw_spdx": "MIT",
"file_present": true,
"scorecard_found": true,
"profile_has_license": true
},
"activity": {
"releases": [
{
"tag": "nom-exif-v3.6.1",
"kind": "other",
"published_at": "2026-06-11T14:36:21Z"
},
{
"tag": "rexiftool-v0.2.4",
"kind": "other",
"published_at": "2026-06-11T14:35:56Z"
},
{
"tag": "v3.6.0",
"kind": "minor",
"published_at": "2026-05-28T14:25:18Z"
},
{
"tag": "v3.5.1",
"kind": "patch",
"published_at": "2026-05-28T14:10:23Z"
},
{
"tag": "rexiftool-v0.2.3",
"kind": "other",
"published_at": "2026-05-23T10:35:47Z"
},
{
"tag": "v3.5.0",
"kind": "minor",
"published_at": "2026-05-23T10:40:51Z"
},
{
"tag": "rexiftool-v0.2.2",
"kind": "other",
"published_at": "2026-05-20T07:58:15Z"
},
{
"tag": "v3.4.2",
"kind": "patch",
"published_at": "2026-05-20T08:08:22Z"
},
{
"tag": "rexiftool-v0.2.1",
"kind": "other",
"published_at": "2026-05-12T08:40:16Z"
},
{
"tag": "v3.4.1",
"kind": "patch",
"published_at": "2026-05-12T08:29:39Z"
},
{
"tag": "rexiftool-v0.2.0",
"kind": "other",
"published_at": "2026-05-10T15:30:53Z"
},
{
"tag": "v3.4.0",
"kind": "minor",
"published_at": "2026-05-10T15:56:34Z"
},
{
"tag": "rexiftool-v0.1.0",
"kind": "other",
"published_at": "2026-05-10T14:19:37Z"
},
{
"tag": "v3.3.0",
"kind": "minor",
"published_at": "2026-05-10T13:14:02Z"
},
{
"tag": "v3.2.0",
"kind": "minor",
"published_at": "2026-05-09T15:18:53Z"
},
{
"tag": "v3.1.1",
"kind": "patch",
"published_at": "2026-05-09T14:54:46Z"
},
{
"tag": "v3.1.0",
"kind": "minor",
"published_at": "2026-05-09T14:54:35Z"
},
{
"tag": "v3.0.0",
"kind": "major",
"published_at": "2026-05-09T10:57:25Z"
},
{
"tag": "v2.8.0",
"kind": "minor",
"published_at": "2026-05-06T10:07:44Z"
},
{
"tag": "v2.7.0",
"kind": "minor",
"published_at": "2026-02-02T06:13:26Z"
},
{
"tag": "v2.6.1",
"kind": "patch",
"published_at": "2026-01-31T01:43:50Z"
},
{
"tag": "v2.6.0",
"kind": "minor",
"published_at": "2026-01-14T12:15:18Z"
},
{
"tag": "v2.5.4",
"kind": "patch",
"published_at": "2025-06-08T01:34:48Z"
},
{
"tag": "v2.5.3",
"kind": "patch",
"published_at": "2025-06-07T15:25:06Z"
},
{
"tag": "v2.5.2",
"kind": "patch",
"published_at": "2025-06-07T13:36:37Z"
},
{
"tag": "v2.5.1",
"kind": "patch",
"published_at": "2025-03-13T10:58:49Z"
},
{
"tag": "v2.4.2",
"kind": "patch",
"published_at": "2025-02-24T14:44:16Z"
},
{
"tag": "v2.3.1",
"kind": "patch",
"published_at": "2025-02-09T00:30:23Z"
},
{
"tag": "v2.3.0",
"kind": "minor",
"published_at": "2025-01-15T04:58:13Z"
},
{
"tag": "v2.2.1",
"kind": "patch",
"published_at": "2024-10-23T15:14:23Z"
},
{
"tag": "v2.1.1",
"kind": "patch",
"published_at": "2024-10-23T12:08:06Z"
},
{
"tag": "v2.1.0",
"kind": "minor",
"published_at": "2024-10-07T03:10:19Z"
},
{
"tag": "v2.0.2",
"kind": "patch",
"published_at": "2024-09-18T03:25:23Z"
},
{
"tag": "v2.0.0",
"kind": "major",
"published_at": "2024-09-17T15:43:13Z"
},
{
"tag": "v1.5.2",
"kind": "patch",
"published_at": "2024-09-05T01:51:20Z"
},
{
"tag": "v1.5.1",
"kind": "patch",
"published_at": "2024-08-31T08:52:25Z"
},
{
"tag": "v1.5.0",
"kind": "minor",
"published_at": "2024-08-31T04:40:22Z"
},
{
"tag": "v1.4.1",
"kind": "patch",
"published_at": "2024-08-25T04:35:46Z"
},
{
"tag": "v1.4.0",
"kind": "minor",
"published_at": "2024-08-25T04:27:41Z"
},
{
"tag": "v1.3.0",
"kind": "minor",
"published_at": "2024-07-25T10:01:52Z"
},
{
"tag": "v1.2.6",
"kind": "patch",
"published_at": "2024-07-13T11:11:02Z"
},
{
"tag": "v1.2.5",
"kind": "patch",
"published_at": "2024-07-13T02:34:44Z"
},
{
"tag": "v1.2.4",
"kind": "patch",
"published_at": "2024-07-12T15:18:29Z"
},
{
"tag": "v1.2.3",
"kind": "patch",
"published_at": "2024-07-12T07:56:47Z"
},
{
"tag": "v1.0.1",
"kind": "patch",
"published_at": "2024-02-23T14:30:20Z"
},
{
"tag": "v1.0.0",
"kind": "major",
"published_at": "2024-02-20T08:45:48Z"
},
{
"tag": "v0.2.0",
"kind": "minor",
"published_at": "2024-02-18T09:35:39Z"
},
{
"tag": "v0.1.1",
"kind": "patch",
"published_at": "2024-02-15T06:24:12Z"
}
],
"recent_commits": [
{
"oid": "d0b1412d6f115b256fdbc47ba71475f509186745",
"body": null,
"is_bot": false,
"headline": "release: nom-exif v3.6.1 + rexiftool v0.2.4",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-06-11T14:38:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "13fbaa4e98a9f33c0158e53a1573571df1ffd5e3",
"body": "* feat: short-circuit MIME detection for legacy QuickTime pnot files\n\nFiles from early consumer cameras (Nikon COOLPIX S5/P5000, Casio EX-Z50,\nFujifilm FinePix older AVI/MOV mixes, etc., ca. 2003-2008) start with a\n`pnot` (preview) atom instead of `ftyp`. Such files were rejected by\n`parse_bmff_mime\n[…]\n track parsing\nworks unchanged.\n\nVerified against a real-world mdat-first MOV (moov at EOF, 2.8 MB\nmdat): CreateDate matches ExifTool output.\n\n---------\n\nCo-authored-by: enjoypi <sre@happyfactory.com>",
"is_bot": false,
"headline": "feat: short-circuit MIME detection for legacy QuickTime pnot files (#59)",
"author_name": "Fan",
"author_login": "only1zf",
"committed_at": "2026-06-11T14:31:10Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "a65033ae1829418772284da44ac68915bb6c6294",
"body": "…+ slice coercion fix",
"is_bot": false,
"headline": "release: nom-exif v3.6.0 (2026-05-28) — tokio/tokio-fs feature split …",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-28T14:22:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b839e60e3ccb81bffcc5827b8c48ab499707eaa2",
"body": null,
"is_bot": false,
"headline": "docs: clarify async feature split in module-level doc",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-28T14:20:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6ab4e6577ddb4937a78842c06800106a5807e4b5",
"body": "so that wasm32-unknown-unknown and similar targets\ncan use the tokio feature for AsyncMediaSource",
"is_bot": false,
"headline": "refactor!: split up tokio feature into tokio and tokio-fs",
"author_name": "Enduriel",
"author_login": "Enduriel",
"committed_at": "2026-05-28T14:17:30Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6150bcb317308acfa709d81b0ec08ac8ebb1492d",
"body": "…ompat",
"is_bot": false,
"headline": "release: nom-exif v3.5.1 (2026-05-28) — slice coercion fix for rkyv c…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-28T14:05:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "872ade504d3940b94bc2c8cbcf72b57d60f8684c",
"body": "Coverage increased from 72.59% to 95.15%, well above the 86% CI threshold.\nTests cover: container_motion_photo_offset (XMP Container Directory),\nscan_motion_photo error/incomplete branches, parse_motion_photo_offset\nedge cases (MicroVideo gate, invalid offsets), parse_segment size<2,\nextract_exif_data payload<=6, memchr_subslice empty/long needle,\ncheck_jpeg malformed input, and extract_attr_value edge cases.",
"is_bot": false,
"headline": "test: add 36 unit tests for jpeg.rs motion photo and edge case paths",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-28T13:59:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "dad49e02b13322c75399481cff0b5f106eb33dd6",
"body": "Comparing `extract_attr_value()` (which returns `Option<&[u8]>`) against byte-string literals relied on automatic unsizing coercion from `&[u8; N]` to `&[u8]`. This fails when a crate like rkyv is in the dependency graph due to its blanket `PartialEq<ArchivedOption<T>>` impl adding a trait resolution candidate.\\n\\nCoerce the literals to slices explicitly via `&b\"MotionPhoto\"[..]`.",
"is_bot": false,
"headline": "fix: explicit slice coercion in MotionPhoto attribute comparison",
"author_name": "Enduriel",
"author_login": "Enduriel",
"committed_at": "2026-05-28T13:31:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9bd5ac7ecc3509f0f2a26214ad34536c1a57e98f",
"body": "…er + LensSerialNumber)",
"is_bot": false,
"headline": "release(rexiftool): v0.2.3 — pick up nom-exif 3.5.0 (CameraSerialNumb…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-23T10:34:18Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "acb56d18f4f73f00d7211fa0de320f3d2955cd28",
"body": "…alNumber EXIF tags\n\nMinor release adding two standard EXIF tag definitions:\n\n- CameraSerialNumber (0xa431) — camera body serial number (#56)\n- LensSerialNumber (0xa435) — lens serial number (#57)\n\nBoth additive only, fully backward compatible.",
"is_bot": false,
"headline": "release: nom-exif v3.5.0 (2026-05-23) — CameraSerialNumber + LensSeri…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-23T10:30:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f0030f00024d48abf770c916e1975c82a015f648",
"body": null,
"is_bot": false,
"headline": "Add LensSerialNumber",
"author_name": "Mahmoud Al-Qudsi",
"author_login": "mqudsi",
"committed_at": "2026-05-23T09:27:24Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bc139f1515286a19778f9f184677ddc442690aa7",
"body": null,
"is_bot": false,
"headline": "Add CameraSerialNumber",
"author_name": "Mahmoud Al-Qudsi",
"author_login": "mqudsi",
"committed_at": "2026-05-23T09:25:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "383d0f60cb3f9437df6fcdcaeecde4ef9f369bd7",
"body": null,
"is_bot": false,
"headline": "release(rexiftool): v0.2.2 — pick up nom-exif 3.4.2 PNG streaming fix",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-20T07:57:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "22f41ab80418dfa3478b9efddbfa8e5b8a10a492",
"body": "…Kind plumbing\n\nPatch release covering two fixes:\n\n- #55: streaming-mode PNG parsing failed on every real PNG with the\n confusing \"malformed iso-bmff box: PNG: bad signature\" error. The\n chunk walker now requests the correct skip distance and threads\n PngPastSignature state so the resumed call do\n[…]\ne at the type level; behaviour change is \"wrong error\nlabel → correct error label\" — strict callers matching specific\nMalformedKind values may see different labels for non-PNG/non-TIFF\nparse failures.",
"is_bot": false,
"headline": "release: nom-exif v3.4.2 (2026-05-20) — PNG streaming fix + Malformed…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-20T07:56:51Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "691c2ed81478865536aaaa1da8718f61580bcd03",
"body": "…t each call site\n\nCode-review follow-up to 3100a4b. That commit reshaped\n`ParsedError::Failed` to carry `MalformedKind` and removed the\nhardcoded `IsoBmffBox` fallback in `From<ParsedError> for Error`, but\nleft the parallel `From<nom::Err<...>> for crate::Error` blanket impl\nin place — and that one\n[…]\n .. })` assertions in\n src/ebml/webm.rs now also pin `kind: MalformedKind::EbmlElement`.\n\nCosmetic:\n- Unify the \"skip too many bytes\" comment between parser.rs and\n parser_async.rs (was asymmetric).",
"is_bot": false,
"headline": "refactor(error): drop blanket nom::Err → Error fallback, plumb kind a…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-20T07:34:17Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "3100a4b9a1b04ec2a37b6f383ccafad20e3edad9",
"body": "…x fallback\n\n`From<ParsedError> for Error` used to hardcode `MalformedKind::IsoBmffBox`\nas the fallback label for every parse failure, regardless of which\nstructural unit actually failed. The pre-existing TODO comment called\nfor \"plumbing the actual MalformedKind through ParsedError so this\nfallback\n[…]\nnd_to_top_level_error`\n locks the conversion contract.\n\nFollow-up to #55. End-to-end PNG errors now surface as\n`Error::Malformed { kind: PngChunk, ... }` instead of the misleading\n`IsoBmffBox` label.",
"is_bot": false,
"headline": "fix(error): plumb MalformedKind through ParsedError, retire IsoBmffBo…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-20T03:23:30Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "a935b8727bc57105a4347dc3b8939f8ce8e09eed",
"body": "Two coupled defects in `extract_chunks` made every real-world PNG (any\nfile with a non-stripped IDAT) fail in streaming mode with the\nconfusing `malformed iso-bmff box: PNG: bad signature` error:\n\n1. `ClearAndSkip(total - remaining)` requested the wrong skip distance.\n The parser interprets `Clear\n[…]\nth_state`)\nand a tightened skip-distance assertion in `extract_chunks_skips_large_idat`.\n\nThe pre-existing `IsoBmffBox` mislabel in `error.rs:146` (TODO marked)\nis orthogonal and left for a follow-up.",
"is_bot": false,
"headline": "fix(png): streaming chunk walker strands mid-IDAT on retry (#55)",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-20T03:03:51Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "12de89950b6d3798419981a9a61f284178712da5",
"body": "- actions/checkout: bump build + fuzz jobs from v3 to v4 (coverage job\n already on v4; v3 inconsistency would surface as deprecation warnings)\n- workflow: document fail_ci_if_error: false — Codecov is badge-only,\n gate is the separate --fail-under-lines step\n- webm test: correct misleading comment in webm_rejects_non_webm_input\n (JPEG is rejected at doc_type parse, not at the NotWebmFile branch);\n tighten assertion to matches!(err, ParsingError::Failed(_))",
"is_bot": false,
"headline": "ci/test: address aggregate review nits",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T13:44:01Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "41fccb3e707d7cef3d9e1e24650f144023d898bc",
"body": "…h_else\n\n- jpeg.rs:325 `loop { let Some(...) = ... else break; }` → `while let Some(...) = ...`\n (pre-existing, flagged by rust 1.95 clippy)\n- cr3_moov.rs: collapse `match` with vacuous Err arm → `if let Ok(...)`\n- values.rs: alias `fn(&EntryValue) -> bool` to `Check` in two test fixtures\n to satisfy `type_complexity` under `--tests --all-features`",
"is_bot": false,
"headline": "chore(clippy): silence while_let_loop / type_complexity / single_matc…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T13:40:21Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b5d27a77f308b9c9f81431ced2dee6ccb515685c",
"body": "Baseline after spot-fix tests: 88.54% line coverage. Threshold set at\nfloor(achieved) - 2 to leave a small headroom for non-critical PRs while\ncatching genuine regressions.\n\ncargo-llvm-cov 0.8.6 silently no-ops --fail-under-lines when combined\nwith --lcov, so the threshold check runs as a separate\n`cargo llvm-cov report --text` step.",
"is_bot": false,
"headline": "ci(coverage): enforce --fail-under-lines 86 threshold",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T11:02:45Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c65ee1a5e196563078fad78a351b48291c6db0f5",
"body": "…eta.rs",
"is_bot": false,
"headline": "refactor(bbox): delete dead idat.rs and its commented references in m…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:59:19Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "244b52bff3032af1e5a44093d45d57c4cea33b80",
"body": null,
"is_bot": false,
"headline": "test(webm): tighten NotWebmFile assert and cover Invalid* From arms",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:57:55Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e691237d1872da9aab0a02c009be03ff21d09f89",
"body": "…appy path",
"is_bot": false,
"headline": "test(webm): cover NotWebmFile, truncation/Need paths, multi-fixture h…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:52:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d125722c54208a06f4e67b1feb8cf67c2474eb39",
"body": null,
"is_bot": false,
"headline": "test(cr3): assert CMT1 explicitly in second-pass test",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:48:34Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2e1e9f679345e1983b0239dfc9c3d3d639a57588",
"body": "…-pass",
"is_bot": false,
"headline": "test(cr3): cover truncation paths, CMT range extraction, state second…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:45:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4024f54f1e8f3a689521a3b07c645589518bd58f",
"body": null,
"is_bot": false,
"headline": "test(values): tighten variant_default checks for IRational/F32/F64",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:44:14Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "61227ec62b0c808005d82337fed0fa4dfefe756c",
"body": null,
"is_bot": false,
"headline": "test(values): remove spec-out-of-scope datetime/accessor/rational tests",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:40:45Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0b420da630e751d34a19507f1446724ab5546cc6",
"body": "… arms",
"is_bot": false,
"headline": "test(values): cover InvalidShape arms, variant_default, accessor None…",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:35:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "14b55512087fbde02e294bb7bf9613cdca30ff2d",
"body": null,
"is_bot": false,
"headline": "test(heif): tighten should_panic with expected message",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:30:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2810a44b0dbdc948a2929670e134e9a0825df8c0",
"body": null,
"is_bot": false,
"headline": "test(heif): cover ClearAndSkip, bad-ftyp, no-meta, and second-pass paths",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:25:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "444bd63ccaeae2a8cddc0707d0e70d850676cd2d",
"body": null,
"is_bot": false,
"headline": "test(travel): address review — rename misleading tests, drop le flag",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:19:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d670ba58b5d0346e1f3daf2390b8016fa2bddd95",
"body": null,
"is_bot": false,
"headline": "test(travel): cover tag==0, format-error, sub-IFD, depth/offset guards",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:13:31Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5f02ce9971db1aa19e477433de18a9a2705908df",
"body": "Seven tasks: one per low-coverage file (travel/heif/values/cr3/webm),\nplus idat.rs deletion and a --fail-under-lines CI gate.",
"is_bot": false,
"headline": "docs(plan): test coverage spot-fix implementation plan",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T10:05:19Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "85d38a0d3e459487a66fab33d769635db951c6ce",
"body": "…+ CI gate)\n\nBaseline 84.72%; target ~87% by adding tests to 5 low-coverage modules,\nremoving dead idat.rs, and adding a --fail-under-lines gate to CI.",
"is_bot": false,
"headline": "docs(spec): test coverage spot-fix plan (travel/heif/cr3/values/webm …",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T09:58:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5f1640933896df98fea28dd63cf7db647576b147",
"body": "Runs `cargo llvm-cov --package nom-exif --all-features` in CI and uploads\nlcov.info to Codecov. Tokenless upload for now (public repo); add\nCODECOV_TOKEN secret later if rate-limiting becomes an issue.",
"is_bot": false,
"headline": "ci(coverage): add cargo-llvm-cov job and codecov badge",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-12T09:36:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "94e84dde0a7860d7d6ba2f1999411670e9a62efe",
"body": "Bumps the minimum nom-exif requirement from \"3.4\" to \"3.4.1\" so\n`cargo install rexiftool --locked` and the pre-built GitHub Releases\nbinaries actually carry the Sony A7C2 HIF GPS fix (issue #50). Default\n`cargo install rexiftool` would already pull 3.4.1 via the caret\nrange, but locked / binary inst\n[…]\nxif lib tag for\nthe GitHub \"Latest release\" badge. (Today rexiftool-v0.2.0 wasn't\nlabelled Latest only because v3.4.0 outranks 0.2.0 in semver; future\nrexiftool 1.x could accidentally promote itself.)",
"is_bot": false,
"headline": "release(rexiftool): v0.2.1 — pick up nom-exif 3.4.1 GPS sub-IFD fix",
"author_name": "min",
"author_login": null,
"committed_at": "2026-05-12T08:39:12Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "75163f937fff940eb722f99925fe55da0e3c987a",
"body": "Patch release for #50: cameras that put GPSVersionID (tag 0) first in\nthe GPS sub-IFD (notably Sony A7C2 HIF) had every GPS field silently\ndropped because the IFD iterator's defensive `tag == 0` short-circuit\nfired on the legitimate version entry. The bail-out is now gated on\n\"not inside the GPS sub-IFD\"; the malformed-IFD defense survives\nelsewhere and parse_gps() recovers the full sub-IFD.\n\nPure bugfix, no API or behavior change for non-affected files.",
"is_bot": false,
"headline": "release: nom-exif v3.4.1 (2026-05-12) — Sony A7C2 HIF GPS fix",
"author_name": "min",
"author_login": null,
"committed_at": "2026-05-12T08:24:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "476f24f16d3cac0a081a350610beefbeb43c7d7b",
"body": "… trap\n\n`IfdHeaderTravel` currently only inspects `sub_ifd_offset`, so its\n`tag == 0` short-circuit is observationally inert. If the struct ever\nstarts emitting entry values, that short-circuit will replay the bug\nfixed in 921ca3f for `IfdIter`. Point the reader at the gated version\nand at issue #50.",
"is_bot": false,
"headline": "docs(travel): warn future maintainers about the tag==0 / GPSVersionID…",
"author_name": "min",
"author_login": null,
"committed_at": "2026-05-12T08:16:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "921ca3fd2c4a0a60f29cb0bc44436d4582aaf26d",
"body": "`IfdIter::parse_tag_entry` short-circuited on `tag == 0` as a defensive\nguard against zero-padded malformed IFDs. But tag 0 is also the\nlegitimate GPSVersionID — the spec-defined first entry of the GPS\nsub-IFD. Aborting iteration there caused the whole sub-IFD to be\ndropped, silently losing every GP\n[…]\n-out on `!self.is_gps_subifd()` so the defense survives in\nnon-GPS contexts while GPSVersionID parses normally. Adds a regression\ntest built from a hand-crafted minimal little-endian TIFF.\n\nFixes #50.",
"is_bot": false,
"headline": "fix(exif): parse GPS sub-IFD when GPSVersionID is its first entry",
"author_name": "min",
"author_login": null,
"committed_at": "2026-05-12T08:13:54Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "20f2305f42972a3893d78e5ea86a9a907a205345",
"body": "BREAKING for `serde` feature: structured per-variant Serialize for\nEntryValue. Previously Serialize delegated to Display, which (a)\ntruncated arrays after 8/3 elements with `...` (silently losing data)\nand (b) emitted opaque strings like \"175/100 (1.7500)\" for rationals.\nThe new shape:\n\n- Scalar num\n[…]\nr `Name[...]` form.\n\nTest fixtures (testdata/exif.jpg.txt, canon-r6.cr3.sorted.txt) updated\nto the new format.\n\nBump rexiftool's nom-exif dep to 3.4 so a published rexiftool 0.2.0\npulls the fixed lib.",
"is_bot": false,
"headline": "release: nom-exif v3.4.0 — structured serde + Display redesign",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T15:28:58Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "c34a8262e08ada28c4b336e9cf89bcbe114a13dc",
"body": "Bump rexiftool 0.1.0 → 0.2.0 and document the new flags / behavior:\n\n- `:` separator instead of `=>`\n- IFD1 (thumbnail) hidden by default; `--with-thumbnail` opts in\n- long values truncated by default; `--full` opts out\n\nJSON output is unchanged from v0.1.0 (always full, always includes\nIFD1).",
"is_bot": false,
"headline": "release(rexiftool): v0.2.0 — output cleanup, IFD1 hidden by default",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T15:05:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f3b0e3ca0224e4325a8952984991f1715b83294e",
"body": "Behavior changes (default human-readable output only; --json unaffected):\n\n- Hide IFD1 (thumbnail) entries by default. They mostly duplicate IFD0\n (XResolution, ExifImageWidth, ResolutionUnit, …), which made the\n output look like it had buggy duplicate keys. Add `--with-thumbnail`\n to opt back in\n[…]\n\n Embedded Track / Format Metadata blocks visually separate.\n- Render empty string values as `(empty)` instead of a bare trailing\n separator, so `OffsetTime: ` no longer looks like a render failure.",
"is_bot": false,
"headline": "rexiftool: cleaner human output — `:` separator, truncation, IFD1 hidden",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T15:02:43Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0fd122e080f6231dd6480e453840446fd6544c27",
"body": "- Section headers all share a 48-char `-- Title ---` style; show\n `-- EXIF --` only when an Embedded Track or Format Metadata\n section also follows, so single-section output stays uncluttered.\n- Key column auto-sizes from the widest tag (clamped 32..=48) so\n long MakerNote/Unknown keys no longer break alignment.\n- Directory mode prints `File: {path}` via `Path::display()` instead\n of the debug-quoted `OsStr`, and reuses the same divider width.",
"is_bot": false,
"headline": "rexiftool: unify print format across sections",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T14:49:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "660e9143f4b21458c8703c296ffb4b463fa7fca1",
"body": null,
"is_bot": false,
"headline": "chore: remove AGENTS.md",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T14:43:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3980e646d5265a09a2fc8196226827b85cc47c21",
"body": "Triggers on `v[0-9]*` tag push (excludes rexiftool-v* and other\nnon-semver tags). Extracts the matching `## nom-exif vX.Y.Z`\nsection from CHANGELOG.md as the release body, appends a footer\nlinking to the full changelog and crates.io page, and creates a\ndraft release via gh CLI. Manual review + publish required.\n\nSkip: cargo publish stays manual — crates.io is irreversible and\nrelease notes for marquee versions benefit from human polish.",
"is_bot": false,
"headline": "ci: auto-create draft GitHub release on lib v* tag push",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T14:35:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9f30863c35609b66ab101f7716e127f6dbe77385",
"body": null,
"is_bot": false,
"headline": "docs: tighten Supported File Types into two-line summary",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T14:13:45Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "18425ac2f92467668103cdc21ca92a1fe7740d4c",
"body": null,
"is_bot": false,
"headline": "docs: list AVIF in README + lib.rs supported formats",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T14:11:14Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b134f59af020a41505c6a2ce60be1abce45abe1f",
"body": null,
"is_bot": false,
"headline": "docs: trim README rexiftool section, defer to crate README",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T14:07:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "81c61cbefe8a26a5a44c690529479b9f26d38104",
"body": "Promote examples/rexiftool.rs to a workspace member crate at\ncrates/rexiftool/ (v0.1.0). Users can now install the CLI via:\n\n cargo install rexiftool\n\nThe lib's Cargo.toml drops clap and serde_json from dev-dependencies\n(they were CLI-only) and removes the [[example]] block. The CLI gets\nits own RE\n[…]\n tag\npush, cross-builds release binaries for macOS x86_64, macOS aarch64,\nLinux x86_64, and Windows x86_64; packages each with the LICENSE +\nREADME; and uploads them to the GitHub release.\n\nCloses #3.",
"is_bot": false,
"headline": "feat: split rexiftool into a standalone CLI crate",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T13:59:47Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9444ead8de88f23b666e89fa38f421f2618de9a0",
"body": "- Move Motion Photos section ahead of in-memory / format-specific\n bits in both README and lib.rs (it is the headline differentiator,\n not an appendix).\n- Drop deprecation noise from user-facing docs (already in CHANGELOG):\n the \"Migration note (v3.3+)\" block in README, the \"v3.0-style API\n (dep\n[…]\n verbose\n clone+into+map EXIF extraction since Quick Start already shows it.\n- Update lib.rs API surface to point at MediaSource::from_memory\n instead of listing the deprecated *_from_bytes helpers.",
"is_bot": false,
"headline": "docs: restructure README + lib.rs around Motion Photos",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T13:42:11Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "918d71b1f6cedeeb55252be3927126fc3365d1e5",
"body": null,
"is_bot": false,
"headline": "release: v3.3.0 (2026-05-10) — PNG support + source-input unification",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T13:12:23Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a7fad88db8bfed0cc79e0e5f44b6653c4a272c48",
"body": "Sync parse_exif (commit 70768d2) and async parse_image_metadata_async\n(commit 51876d0) both pick up PNG-scoped EOF tolerance, but async\nparse_exif_async was missed when the async memory mode landed.\nEffect: a 117-byte text-only.png passed to parse_exif_async via\ntokio::fs::File would surface Unexpec\n[…]\na_async via seekable → Ok with format\n\nThese tests actually traverse fill_buf and the PNG EOF-tolerance\ngate, and would catch any future regression that drops the gate\nfrom one of the four call sites.",
"is_bot": false,
"headline": "fix(async): parse_exif_async streaming branch missing PNG EOF tolerance",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T13:04:57Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e7a3e7ae1ea4bc2495f5de235f984106306e9da2",
"body": "The async-memory deferral noted in P0 task 0.4 is no longer relevant —\nAsyncMediaSource gained from_memory and the three parse_*_async\nmethods all accept memory-mode sources with the same zero-copy\nguarantee as sync.",
"is_bot": false,
"headline": "docs(changelog): document AsyncMediaSource::from_memory",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T12:56:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a1436e019d2a4026df94cf28dfaa65dbc7e6e96c",
"body": "8 new tests covering the parity with the sync memory-mode route:\n\n- AsyncMediaSource::from_memory accepts JPEG / MOV / rejects unknown\n- parse_exif_async on memory JPG returns the expected EXIF\n- parse_exif_async memory mode does not poison the recycle cache\n (parser.state.cached_ptr_for_test() sta\n[…]\ninfo\n- parse_image_metadata_async on memory PNG (eXIf + tEXt) returns\n both halves\n- parse_image_metadata_async on memory text-only.png exercises the\n PNG-scoped EOF tolerance through the async path",
"is_bot": false,
"headline": "test(async): coverage for AsyncMediaSource::from_memory",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T12:56:16Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "51876d092a017617be9bd04dd58c63c2ae44dc1b",
"body": "Mirrors the v3.3 sync work that was deferred for async in P0:\n\n- AsyncMediaSource gains a `memory: Option<bytes::Bytes>` field.\n- AsyncMediaSource<tokio::io::Empty>::from_memory(bytes) is the new\n zero-copy constructor (analog of MediaSource::from_memory). Returns\n AsyncMediaSource<tokio::io::Empt\n[…]\nEOF tolerance\n the sync version has (fix 70768d2), so tEXt-only PNGs <128 bytes\n parse correctly through the async path too.\n\nNo new dependencies; no public API removals. Tests in subsequent\ncommit.",
"is_bot": false,
"headline": "feat(async): AsyncMediaSource gains memory mode",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T12:54:59Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "479cc3324c1c4deec64adc1c2f74b8924ac133fe",
"body": "Previously the image branch called parse_exif then re-opened the\nfile to call parse_image_metadata for the format-metadata section —\ntwo file opens and two passes through the parser per PNG. Use a\nsingle parse_image_metadata call (which also returns the EXIF iter\nfor non-PNG images via its non-PNG f\n[…]\nall existing tests; smoke-tested on\ntestdata/exif.png (EXIF + Format Metadata), testdata/text-only.png\n(Format Metadata only), testdata/exif.jpg (EXIF only),\ntestdata/exif.png --no-format (EXIF only).",
"is_bot": false,
"headline": "refactor(rexiftool): single parse_image_metadata call per image",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T12:34:02Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "70768d2ed80b0a9e91f48e69428d899bf5de37ce",
"body": "parse_exif and parse_image_metadata previously swallowed\nUnexpectedEof from the initial fill_buf whenever the buffer was\nnon-empty. That widened the contract for every format: a 100-byte\ntruncated JPEG (whose mime-detection prefill emptied the reader)\nsilently returned ExifNotFound instead of Unexpe\n[…]\nnexpectedEof for\ntiny truncations.\n\nGate the EOF-swallow on `mime_img == Png` and add a regression test\nusing testdata/compatible-brands.heic (36 bytes) to lock in the\nstrict-EOF contract for non-PNG.",
"is_bot": false,
"headline": "fix(parser): scope EOF tolerance to PNG only",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T12:32:59Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "92c21ba1ff36cf00a7ce47d63f0a602e28ac8baf",
"body": "extract_chunks computed `8 + length as usize + 4` per chunk, where\nlength is a u32 from the wire. On 32-bit targets this wraps when\nlength is near u32::MAX, producing a tiny `total` that passes the\nlength check and then advances `cursor` by a wrapped value — risk of\nre-parsing the same chunk forever or reading out of bounds.\n\nUse checked_add and bail with ParsingError::Failed on overflow.\nThe defensive test now also exercises the unknown-chunk path with\nu32::MAX length.",
"is_bot": false,
"headline": "fix(png): guard against chunk-length integer overflow on 32-bit",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T12:26:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d4acee09369576043aa55aa997e53faae704a34a",
"body": "The return type Result<(Option<ExifIter>, Vec<(String,String)>),\ncrate::Error> triggers clippy::type_complexity. Added\n#[allow(clippy::type_complexity)] on both the sync and async variants\nso `cargo clippy -- -D warnings` passes cleanly.",
"is_bot": false,
"headline": "fix(clippy): suppress type_complexity on parse_png_full + async",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:59:23Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c45ede60f5cd5299b75ce163c364073a76e90f76",
"body": "ExifNotFound (e.g. PNG with only tEXt chunks) was propagating as a\nhard error before format metadata could be printed. Now treat it as\nempty EXIF and continue to the format metadata section, so\ntext-only.png shows \"-- Format Metadata --\" without an EXIF section.",
"is_bot": false,
"headline": "fix(rexiftool): show Format Metadata even when image has no EXIF",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:58:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0d54383f79268dcb5a843eb7ce19480817e068b8",
"body": "Three sections under Unreleased:\n- Added: PNG support, parse_image_metadata, from_memory, new\n public types, rexiftool format-metadata printing\n- Deprecated: from_bytes / parse_*_from_bytes / read_*_from_bytes\n family (removed in v4)\n- Notes: read_image_metadata top-level helpers deferred to v4;\n iTXt/zTXt deferred (non-breaking when added)",
"is_bot": false,
"headline": "docs(changelog): v3.3 — PNG support + source-input unification",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:56:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e262650b6399e98412c4cfb64b4ff8ec40ee7e1f",
"body": "Mirrors the existing -- Embedded Track -- pattern. Adds:\n- --no-format flag to suppress the new section\n- JSON dump nests text chunks under \"_format\" key\n\nThe text chunks come from MediaParser::parse_image_metadata's\nImageFormatMetadata::Png(PngTextChunks). For non-PNG inputs the\nsection is omitted entirely.",
"is_bot": false,
"headline": "feat(rexiftool): print PNG tEXt chunks under -- Format Metadata --",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:56:23Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1bfb257aa0c9ed2743c99ded2246d3e69d32359f",
"body": "Adds the highlights bullet about PNG and a new \"Image metadata\nbeyond EXIF\" section with a runnable doctest example showing\nparse_image_metadata + ImageFormatMetadata::Png.",
"is_bot": false,
"headline": "docs: lib.rs module docstring — PNG + parse_image_metadata",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:54:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "30c39fab344f68372953bccb5e12792edcbc5546",
"body": "Adds .png to \"Supported File Types\" and a new\n\"Format-Specific Metadata\" section demonstrating the\nparse_image_metadata entry point. The example shows EXIF +\nImageFormatMetadata::Png(PngTextChunks) extraction in one call.\n\nNotes that legacy EXIF (Raw profile type *) is transparently\nmerged, and that top-level read_image_metadata helpers are\ndeferred to v4.",
"is_bot": false,
"headline": "docs: README — PNG support + parse_image_metadata example",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:54:23Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fbd76fb8d5822f66aab71e1a145cc24b76704649",
"body": "Three new fixtures:\n- exif-legacy.png: Raw profile type exif only (ImageMagick style)\n- exif-legacy-app1.png: Raw profile type APP1 with \"Exif\\0\\0\" prefix\n- exif-both.png: eXIf + Raw profile type exif (different content)\n\nTests verify:\n- read_exif transparently works on legacy-only PNGs\n- parse_image_metadata exposes the raw \"Raw profile type exif\"\n tEXt entry alongside the merged EXIF\n- eXIf takes precedence when both are present",
"is_bot": false,
"headline": "test: integration tests for PNG legacy hex-encoded EXIF",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:51:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "600a400abe8c742b1c1b6bfd974c02005bd591ff",
"body": "Replaces the P4 stub (which returned ExifNotFound for Legacy) with\na real implementation: wrap the owned Vec<u8> in a fresh\nbytes::Bytes and feed to input_into_iter.\n\nThe fresh allocation is acceptable because:\n- Legacy hex-encoded EXIF in PNG is rare in modern files (eXIf path\n is preferred when both exist, and most encoders write eXIf).\n- Legacy TIFF blobs are typically small (a few KB).\n- The eXIf path remains zero-copy via the parser's share_buf.",
"is_bot": false,
"headline": "feat: materialize PngExifSource::Legacy into ExifIter",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:50:34Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "191d39a9cfeb3f3edda45fb41fee4934a618a5f1",
"body": "ImageMagick \"Raw profile type exif\" and Photoshop \"Raw profile type\nAPP1\" tEXt entries are hex-decoded, APP1 prefix stripped, and\nvalidated as TIFF (must have valid byte-order marker + magic 42).\nOn success, surfaced as PngExifSource::Legacy(Vec<u8>).\n\nSource-priority rule:\n 3 (highest) — eXIf chun\n[…]\ns of file order.\nThe original tEXt entry is preserved in text_chunks regardless.\n\nFailed hex-decode / failed TIFF validation → silently drop the\nlegacy candidate (text_chunks still has the raw value).",
"is_bot": false,
"headline": "feat: extract_chunks recognizes legacy EXIF in tEXt chunks",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:49:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e1550a39a0a950ec6b6577e0e38b99eceb3bbb5e",
"body": "Used in the next commit by extract_chunks to recognize legacy\nEXIF embedded in \"Raw profile type {exif,APP1}\" tEXt chunks\n(ImageMagick / Photoshop pattern).\n\ndecode_raw_profile_value handles the 3-line header preamble that\nImageMagick prepends; hex_decode is the underlying primitive\n(tolerates whitespace, validates length parity).",
"is_bot": false,
"headline": "feat: hex_decode + decode_raw_profile_value helpers",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:47:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6dbbfc85f6c8a55765abb5abda620025a0402f49",
"body": "Generates two fixture PNGs programmatically (eXIf+tEXt and tEXt-only)\nusing a real EXIF blob extracted from testdata/exif.jpg's APP1\nsegment.\n\nTests cover:\n- read_exif on EXIF-bearing PNG (file path)\n- read_exif on tEXt-only PNG returns ExifNotFound (contract preserved)\n- parse_image_metadata on EXI\n[…]\n rejected with UnexpectedEof because header detection\nconsumed all bytes before the parse loop could read more.\n\nLegacy hex-encoded EXIF (Raw profile type *) is not yet covered;\nthat lands in phase 5.",
"is_bot": false,
"headline": "test: PNG integration tests for parse_image_metadata + read_exif",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:44:32Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "6e232985b936c83493a031e5df16678d98ac4426",
"body": "Mirrors the sync parse_image_metadata using AsyncBufParser. PNG\nchunk extraction reuses the same png::extract_chunks pure function\nvia the async load_and_parse, so no PNG-specific async code beyond\nthe dispatch wrapper.",
"is_bot": false,
"headline": "feat: parse_image_metadata_async (tokio)",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:39:28Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5d9bde96849bdb4af02d62f3efdbe4c8f2df1c2e",
"body": "Single method handles file/stream/memory sources via the same\n<R: Read> bound used by parse_exif. Internal dispatch:\n- PNG: parse_png_full producing (ExifIter, Vec<(String, String)>)\n- Other images: existing parse_exif_iter; format is None\n- Tracks: ExifNotFound (use parse_track)\n\nReturns ImageMetad\n[…]\nifIter>; convert via .into() for eager\nImageMetadata<Exif> if desired.\n\nFor PNG, the tEXt chunks become ImageFormatMetadata::Png(PngTextChunks).\nIf both exif and format are None, returns ExifNotFound.",
"is_bot": false,
"headline": "feat: MediaParser::parse_image_metadata (sync)",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:38:05Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9f7454dac18fdfdf37ebef1d7f4feb1c9d6c2a03",
"body": "Dispatched at the top of parse_exif_iter (peer to the existing CR3\nspecial case). For PNG inputs, calls png::extract_chunks via\nload_and_parse, then materializes PngExifSource::EXif(Range) into\nan ExifIter using the existing share_buf zero-copy path.\n\nLegacy path (PngExifSource::Legacy) is wired up \n[…]\ns the same shape using AsyncBufParser.\n\nThe MediaMimeImage::Png arm in extract_exif_with_mime is now\nunreachable (the top-level dispatch routes around it). Marked\nunreachable!() to make this explicit.",
"is_bot": false,
"headline": "feat: parse_png_exif_iter — PNG eXIf path integration",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:36:24Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "3361d3989766ac87ea7cd9dda5e42d0e706b4a10",
"body": "Programmatic PNG byte-stream builders: signature + chunks + IEND.\nAlso includes a helper to extract a real TIFF/EXIF blob from\ntestdata/exif.jpg's APP1 segment so we can build PNGs with real\nEXIF data without hand-crafting it.\n\nCRCs are zeroed — nom-exif's PNG parser doesn't verify CRCs\n(consistent with JPEG marker handling).\n\nThis module is shared across integration tests via\n`#[path = \"png_fixtures.rs\"] mod png_fixtures;` includes.",
"is_bot": false,
"headline": "test: add PNG fixture builders for integration tests",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:34:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5201538852838925ad71ac3e6d534300986fa55a",
"body": "Replace forward-references to parse_image_metadata (which lands in P4)\nwith plain text to eliminate cargo doc warnings at this phase.",
"is_bot": false,
"headline": "docs: fix broken intra-doc links in image_metadata module",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:32:22Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "86c448988bbf7c12775964f69d658a4da590f45e",
"body": "Shows the default (eager) form, explicit lazy form, and From\nconversion between them. Verifies type-level composition compiles\ncleanly without users needing to spell out the sealed trait.",
"is_bot": false,
"headline": "docs: add doctest demonstrating ImageMetadata generic forms",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:31:04Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "535d6c4210469becfa04b81a2a53f67cf9e00b64",
"body": "Public types for the new parse_image_metadata entry point:\n- ImageMetadata<E: ExifRepr = Exif> — { exif, format } struct\n- ImageFormatMetadata — #[non_exhaustive] enum with Png variant\n- ExifRepr — sealed marker trait, impl'd by Exif and ExifIter\n- From<ImageMetadata<ExifIter>> for ImageMetadata<Exif> — eager\n conversion\n\nPNG dispatch in MediaParser is still stubbed; the type can be\nconstructed but the parse_image_metadata method that returns it\nlands in phase 4.",
"is_bot": false,
"headline": "feat: add ImageMetadata<E>, ImageFormatMetadata, ExifRepr",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:30:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b88b8af70ac0aaa614b5b5aec26a068d3f825810",
"body": "Opaque wrapper around Vec<(String, String)> exposing get / get_all /\niter / len / is_empty. Latin-1 decoded key/value pairs in PNG file\norder, duplicates preserved.\n\nWill be the payload of ImageFormatMetadata::Png in the next commit.",
"is_bot": false,
"headline": "feat: add PngTextChunks public type",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:29:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b946fc75b57add2b0a9d14accd6cca6554ddd897",
"body": "Crafted inputs (oversized tEXt, u32::MAX length declaration) must\nnot allocate gigabytes or panic. Verifies the size-cap branches in\nthe tEXt arm trigger ClearAndSkip / Need without entering the\nallocation path.",
"is_bot": false,
"headline": "test: defensive bounds in extract_chunks",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:27:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "550226d657aadd4d4fb5e80478ce8488dcea3c54",
"body": "Verifies:\n- Truncated inside eXIf body → ParsingError::Need\n- Truncated before unknown chunk body → ParsingError::ClearAndSkip\n (large IDAT chunks are streaming-friendly: never enter the parse\n buffer, the I/O driver seeks past them)",
"is_bot": false,
"headline": "test: streaming Need/Skip behavior of extract_chunks",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:27:09Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a83862574b3658536bcdc4ff9a69336e55bea21e",
"body": "tEXt chunks split on the first 0x00 byte; both halves Latin-1\ndecoded into String (infallible — every Latin-1 byte maps to a\nUnicode code point). Duplicate keys preserved (PNG spec permits\nthis). Malformed tEXt (no NUL) silently skipped.\n\nDefensive bounds:\n- MAX_TEXT_CHUNK_SIZE = 1 MiB rejects overs\n[…]\n 16 MiB caps cumulative captured size.\n\nLegacy \"Raw profile type *\" recognition (hex-decode → EXIF blob)\ndeferred to phase 5. For now, those entries appear in text_chunks\nverbatim like any other tEXt.",
"is_bot": false,
"headline": "feat: extract_chunks captures tEXt key/value pairs",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:26:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "8b0b778bb1845f084426fb9db06fcee30f01a932",
"body": "When an eXIf chunk is encountered, captures its byte range as\nPngExifSource::EXif(Range). Range is into the input buffer — no\nallocation; the caller can use bytes::Bytes::slice for zero-copy.\n\neXIf has the highest priority: when both eXIf and a legacy\nRaw-profile-type tEXt are present, eXIf wins. Legacy handling\nlands in phase 5.",
"is_bot": false,
"headline": "feat: extract_chunks recognizes eXIf chunk",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:25:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "dfad46dec2742b7325254a75d5c08637aee7587c",
"body": "Validates the PNG 8-byte signature, walks chunk headers, returns on\nIEND. Unknown chunks are skipped via ParsingError::ClearAndSkip\n(letting the I/O driver seek past them). No metadata extraction\nyet — eXIf and tEXt handling come in subsequent commits.",
"is_bot": false,
"headline": "feat: extract_chunks signature + header loop",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:25:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0ece3ebd95a3e830eadb17394acc6fb3d42c97b9",
"body": "Empty module with the public-types shape that the chunk-parser\nfunction will populate. No actual parsing logic yet — that lands\nin subsequent commits in this phase.",
"is_bot": false,
"headline": "feat: scaffold src/png.rs module with PngParseOut + PngExifSource",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:24:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9c2270a8f12ac34bd48a1d13ea2c037a87d14cc8",
"body": "Returns ParsingError::Failed at this stage. Phase 4 replaces this\nwith a special-cased dispatch at the top of parse_exif_iter (peer\nto the existing CR3 special case), so this arm is unreachable in\nthe final design. It exists now only to satisfy the exhaustiveness\ncheck in the match.",
"is_bot": false,
"headline": "feat: add PNG arm to extract_exif_with_mime (stub)",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:22:38Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bef91c5db111c8b83e07367e1b2b9b17207cafd9",
"body": "Adds MediaMimeImage::Png variant and signature check\n(\"\\x89PNG\\r\\n\\x1a\\n\") in MediaMime::try_from. The check sits after\nTIFF detection (defensive ordering — no actual collision exists\nsince PNG/TIFF signatures differ in the first byte).\n\nEXIF extraction for PNG is not yet wired up; that lands in phase 4.\nThis commit only makes the mime layer recognize the format.",
"is_bot": false,
"headline": "feat: detect PNG files by 8-byte signature",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:22:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "464199eb2d40c5d2ddacecb4f0584562a5cd0aa9",
"body": "A 1x1 grayscale PNG with no metadata, used for mime-detection tests.\nReal metadata-bearing PNG fixtures (with eXIf and tEXt chunks) are\ngenerated programmatically by tests/png_fixtures.rs in later phases.\n\nCRCs in the placeholder are zeroed; nom-exif's PNG parser does not\nvalidate CRCs (consistent with how JPEG markers and HEIC boxes are\nhandled today).",
"is_bot": false,
"headline": "test: add minimal testdata/exif.png placeholder",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:21:40Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b231e3fb856e22e5d973737bd6ceb8b804c6c3dc",
"body": "Documents the deprecation of MediaSource::<()>::from_bytes,\nparse_*_from_bytes, and read_*_from_bytes in favor of the unified\nMediaSource::from_memory + parse_* path. Includes the migration\ntable and a side-by-side code example.\n\nRemoval of the deprecated symbols is scheduled for v4.",
"is_bot": false,
"headline": "docs: add v3.0 → v3.3 migration subsection (in-memory bytes API)",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:19:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "804e05a397433bdc4d69a399340e1b4db2c3691e",
"body": "The In-Memory Bytes section now demonstrates the v3.3 unified API.\nAdds a Migration Note paragraph linking to docs/MIGRATION.md for\nexisting v3.0-3.2 users on the deprecated *_from_bytes path.",
"is_bot": false,
"headline": "docs: migrate README in-memory examples to from_memory",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:18:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ea110df6e07ca9c39e11a759c85709ade243d402",
"body": "The module-level docstring's \"Reading from in-memory bytes\" section\nnow demonstrates the v3.3 unified API. A short deprecation note at\nthe section's end points existing read_*_from_bytes users to the\nnew path.",
"is_bot": false,
"headline": "docs: migrate lib.rs in-memory examples to from_memory",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:17:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4eacc220c11c3900de4cfdb1aa6eeeeadf8c5d0a",
"body": "read_exif_from_bytes, read_exif_iter_from_bytes,\nread_track_from_bytes, read_metadata_from_bytes are now deprecated\nand delegate internally to the unified MediaSource::from_memory +\nparse_* path. Removed in v4.",
"is_bot": false,
"headline": "deprecate: top-level read_*_from_bytes helpers",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:17:08Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a321aef6f07b5673ffdd914e1ea0034dee553299",
"body": "The deprecated MediaSource::<()>::from_bytes and parse_*_from_bytes\nmethods are still exercised by the existing test suite (they need\nto keep working through v3.x). Annotate those specific tests with\n#[allow(deprecated)] so deprecation warnings don't pollute CI.\n\nUser-facing deprecation warnings (when callers use the deprecated\nmethods in their own code) are unaffected.",
"is_bot": false,
"headline": "test: silence deprecation warnings in tests of deprecated paths",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:15:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2dbe23a197f134614a502d379041dc0a49d46bcc",
"body": "Marks the v3.0 memory-mode API as deprecated in favor of the unified\nv3.3 form (MediaSource::from_memory + parse_exif / parse_track).\n\nThe deprecated parse_*_from_bytes methods now delegate via the\nprivate into_empty() adapter, eliminating duplicate dispatch logic.\n\nWill be removed in v4. Tests still pass; deprecation warnings inside\nthe crate are addressed in a follow-up commit.",
"is_bot": false,
"headline": "deprecate: MediaSource::<()>::from_bytes and parse_*_from_bytes",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:13:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "af38fa1a234f1975994afe269d5ed69ea4c1039c",
"body": "Mirror the sync refactor: runtime branch on memory.is_some(). The\nexisting async memory-mode entry points (if any) continue to work;\nnew memory-mode async sources can be parsed via the unified path\nonce AsyncMediaSource gains a from_memory constructor (deferred).\n\nNote: AsyncMediaSource has no memory field (no from_bytes constructor\nexists). Steps 2/3 of the plan are skipped per the explicit\ncontingency — async memory-mode is deferred to a follow-up.",
"is_bot": false,
"headline": "refactor: parse_exif_async / parse_track_async handle memory mode",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:12:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0304bc1ad082b5f5d370822b65fa121c10d41819",
"body": "Same shape as the parse_exif refactor in the previous commit:\nruntime branch on ms.memory.is_some() picks the memory-mode (zero\ncopy) or streaming path. Memory-mode JPEG callers do not get\nMotion Photo extraction (matches existing parse_track_from_bytes\nbehavior).",
"is_bot": false,
"headline": "refactor: parse_track handles memory-mode sources internally",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:12:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "db3344dd79e8182ae788eaef8e756a17b6e51b2a",
"body": "parse_exif<R: Read> now branches on ms.memory.is_some() at runtime.\nMemory-mode sources (built via MediaSource::from_memory) take the\nsame zero-copy state.set_memory path that parse_exif_from_bytes uses\ntoday. Streaming sources are unchanged.\n\nExisting parse_exif_from_bytes still works (will be marked\ndeprecated in a later commit).",
"is_bot": false,
"headline": "refactor: parse_exif handles memory-mode sources internally",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T11:10:28Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c132977a960565f69134038f4a68029e4d02752b",
"body": "Returns MediaSource<std::io::Empty>, which satisfies the existing\n<R: Read> bound on parse_exif / parse_track. This sets up the\nupcoming unification — a single parse_* method per \"what to parse\",\nwith no separate _from_bytes sibling.\n\nThe old MediaSource::<()>::from_bytes constructor stays untouched in\nthis commit; it will be deprecated in a follow-up commit once the\nparse_* methods are updated to handle memory-mode sources.",
"is_bot": false,
"headline": "feat: add MediaSource::from_memory for unified memory-mode source",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T06:06:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "87d933e878f5aeee67882c281b57901ec79eb1fe",
"body": "Internal scaffolding for the upcoming source-input unification. Lets\nthe deprecated parse_*_from_bytes methods delegate to the unified\nparse_* methods once those are extended to handle memory mode.\n\nNo behavior change.",
"is_bot": false,
"headline": "refactor: add private MediaSource::<()>::into_empty() adapter",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T05:49:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7e8c907479ebf005be64c32b9807cfa95061f076",
"body": "Master plan + per-phase plans for issue #18 (PNG support). Each\nphase plan is self-contained with bite-sized task steps, exact code\nsnippets, expected command output, and per-task atomic commits.\n\nMaster:\n png-support-master.md — index + cross-phase rules + risk register\n\nPhases:\n P0: source-inpu\n[…]\netadata integration (6 tasks)\n P5: legacy EXIF-in-tEXt (hex-decoded TIFF) (5 tasks)\n P6: docs + CLI + changelog (5 tasks)\n\nReferences spec at docs/superpowers/specs/2026-05-10-png-support-design.md.",
"is_bot": false,
"headline": "docs: add PNG support implementation plan (7 phases)",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T05:29:47Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0b90cad0b5cab22a1dd12ed1f93e3a972fe9a276",
"body": "Updates the PNG support spec to reflect three decisions:\n\n1. FormatMetadata renamed to ImageFormatMetadata. Track-side formats\n (Matroska Chapters/Attachments, MP4 iTunes, ID3, etc.) are real\n future candidates for format-specific extras; mixing them into a\n single FormatMetadata enum would be\n[…]\n release.\n\nBuild sequence becomes 7 phases (0-6), with phase 6 explicitly\ncovering README, lib.rs module docs, doctests, MIGRATION.md, and\nCHANGELOG updates for both PNG and the deprecation migration.",
"is_bot": false,
"headline": "docs: bundle source-input unification into PNG PR",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T04:59:45Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "fe72c522b4a5cdf335c30860c0ac98b8cb432568",
"body": "Drops the three top-level helpers (read_image_metadata,\nread_image_metadata_from_bytes, read_image_metadata_async) from the\nv3.3 plan. Adding them now alongside the existing read_metadata would\ncreate two adjacent top-level entry points with overlapping-but-not-\nidentical semantics, which the v4 Met\n[…]\n the redesigned Metadata enum.\n\nUpdates Scope, public API listing, default-type-parameter rationale,\ntest layers, build-sequence phase 4 and 6, v4 forward-compat note, and\nopen-questions risk wording.",
"is_bot": false,
"headline": "docs: defer top-level read_image_metadata helpers to v4",
"author_name": "Min Deng",
"author_login": "mindeng",
"committed_at": "2026-05-10T03:02:45Z",
"body_truncated": true,
"is_coding_agent": false
}
],
"releases_count": 48,
"commits_last_year": 274,
"latest_release_at": "2026-06-11T14:36:21Z",
"latest_release_tag": "nom-exif-v3.6.1",
"releases_from_tags": false,
"days_since_last_push": 1,
"active_weeks_last_year": 11,
"days_since_latest_release": 40,
"mean_days_between_releases": 3.4
},
"community": {
"has_readme": true,
"has_license": true,
"has_description": true,
"has_contributing": false,
"health_percentage": 42,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"ecosystem": {
"packages": [
{
"name": "nom-exif",
"exists": true,
"license": "non-standard",
"keywords": [
"exif",
"metadata",
"multimedia::audio",
"multimedia::images",
"multimedia::video",
"parsing",
"parser-implementations"
],
"ecosystem": "crates",
"matches_repo": true,
"registry_url": "https://crates.io/crates/nom-exif",
"is_deprecated": false,
"latest_version": "3.6.1",
"repository_url": "https://github.com/mindeng/nom-exif",
"versions_count": 55,
"total_downloads": 155187,
"dependents_count": null,
"deprecation_note": null,
"maintainers_count": null,
"monthly_downloads": 12569,
"first_published_at": "2024-02-15T06:05:01.025567Z",
"latest_published_at": "2026-07-20T03:26:04.072650Z",
"latest_version_yanked": false,
"days_since_latest_publish": 1
}
]
},
"popularity": {
"forks": 16,
"stars": 117,
"watchers": 2,
"fork_history": {
"days": [
{
"date": "2024-07-09",
"count": 1
},
{
"date": "2024-07-10",
"count": 2
},
{
"date": "2024-07-12",
"count": 1
},
{
"date": "2024-07-16",
"count": 1
},
{
"date": "2024-09-13",
"count": 1
},
{
"date": "2024-10-18",
"count": 1
},
{
"date": "2024-11-29",
"count": 1
},
{
"date": "2024-12-29",
"count": 1
},
{
"date": "2025-02-19",
"count": 1
},
{
"date": "2025-06-05",
"count": 1
},
{
"date": "2025-09-04",
"count": 1
},
{
"date": "2026-05-09",
"count": 1
},
{
"date": "2026-05-14",
"count": 1
},
{
"date": "2026-05-23",
"count": 1
},
{
"date": "2026-06-08",
"count": 1
}
],
"complete": true,
"collected": 16,
"total_forks": 16
},
"star_history": {
"days": [
{
"date": "2024-07-04",
"count": 1
},
{
"date": "2024-07-06",
"count": 1
},
{
"date": "2024-07-10",
"count": 10
},
{
"date": "2024-07-11",
"count": 4
},
{
"date": "2024-07-13",
"count": 3
},
{
"date": "2024-07-14",
"count": 1
},
{
"date": "2024-07-15",
"count": 1
},
{
"date": "2024-07-16",
"count": 3
},
{
"date": "2024-07-22",
"count": 6
},
{
"date": "2024-07-31",
"count": 1
},
{
"date": "2024-08-09",
"count": 1
},
{
"date": "2024-08-10",
"count": 1
},
{
"date": "2024-09-04",
"count": 1
},
{
"date": "2024-09-13",
"count": 2
},
{
"date": "2024-09-18",
"count": 4
},
{
"date": "2024-09-19",
"count": 1
},
{
"date": "2024-09-20",
"count": 1
},
{
"date": "2024-10-02",
"count": 1
},
{
"date": "2024-10-10",
"count": 1
},
{
"date": "2024-10-21",
"count": 1
},
{
"date": "2024-10-24",
"count": 1
},
{
"date": "2024-11-11",
"count": 1
},
{
"date": "2024-11-17",
"count": 2
},
{
"date": "2024-11-28",
"count": 1
},
{
"date": "2024-11-29",
"count": 1
},
{
"date": "2024-12-06",
"count": 1
},
{
"date": "2024-12-07",
"count": 1
},
{
"date": "2024-12-24",
"count": 1
},
{
"date": "2024-12-29",
"count": 2
},
{
"date": "2025-01-30",
"count": 1
},
{
"date": "2025-01-31",
"count": 1
},
{
"date": "2025-02-07",
"count": 1
},
{
"date": "2025-03-01",
"count": 2
},
{
"date": "2025-03-08",
"count": 2
},
{
"date": "2025-03-11",
"count": 1
},
{
"date": "2025-04-04",
"count": 1
},
{
"date": "2025-04-09",
"count": 1
},
{
"date": "2025-05-07",
"count": 1
},
{
"date": "2025-06-22",
"count": 1
},
{
"date": "2025-06-27",
"count": 1
},
{
"date": "2025-07-02",
"count": 1
},
{
"date": "2025-07-05",
"count": 1
},
{
"date": "2025-07-14",
"count": 1
},
{
"date": "2025-07-19",
"count": 1
},
{
"date": "2025-08-04",
"count": 1
},
{
"date": "2025-08-20",
"count": 1
},
{
"date": "2025-08-22",
"count": 1
},
{
"date": "2025-08-27",
"count": 1
},
{
"date": "2025-09-25",
"count": 2
},
{
"date": "2025-12-05",
"count": 1
},
{
"date": "2025-12-08",
"count": 1
},
{
"date": "2025-12-27",
"count": 1
},
{
"date": "2026-01-15",
"count": 1
},
{
"date": "2026-01-19",
"count": 1
},
{
"date": "2026-01-27",
"count": 2
},
{
"date": "2026-01-31",
"count": 1
},
{
"date": "2026-02-06",
"count": 1
},
{
"date": "2026-02-09",
"count": 1
},
{
"date": "2026-02-16",
"count": 1
},
{
"date": "2026-02-28",
"count": 1
},
{
"date": "2026-04-11",
"count": 1
},
{
"date": "2026-04-13",
"count": 1
},
{
"date": "2026-04-26",
"count": 1
},
{
"date": "2026-05-09",
"count": 2
},
{
"date": "2026-05-10",
"count": 7
},
{
"date": "2026-05-11",
"count": 1
},
{
"date": "2026-05-12",
"count": 1
},
{
"date": "2026-05-13",
"count": 2
},
{
"date": "2026-05-19",
"count": 2
},
{
"date": "2026-05-28",
"count": 2
},
{
"date": "2026-06-09",
"count": 1
},
{
"date": "2026-06-13",
"count": 1
},
{
"date": "2026-06-20",
"count": 1
},
{
"date": "2026-06-23",
"count": 1
},
{
"date": "2026-06-30",
"count": 1
},
{
"date": "2026-07-07",
"count": 1
}
],
"complete": true,
"collected": 117,
"total_stars": 117
},
"open_issues_and_prs": 5
},
"ai_readiness": {
"has_nix": false,
"example_dirs": [],
"has_llms_txt": false,
"has_dockerfile": false,
"has_mcp_signal": false,
"bootstrap_files": [],
"api_schema_files": [],
"has_devcontainer": false,
"typecheck_configs": [],
"toolchain_manifests": [
"Cargo.toml",
"crates/rexiftool/Cargo.toml",
"fuzz/Cargo.toml"
],
"largest_source_bytes": 87944,
"source_files_sampled": 45,
"oversized_source_files": 1,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"dependencies": {
"manifests": [
"Cargo.toml",
"fuzz/Cargo.toml"
],
"advisories": {
"error": "No resolved dependencies carried a version and a supported ecosystem",
"scope": "repository_graph",
"source": null,
"findings": [],
"collected": false,
"truncated": false,
"by_severity": {},
"advisory_count": 0,
"affected_count": 0,
"assessed_count": 0,
"assessed_package": null,
"unassessed_count": 16,
"direct_affected_count": 0
},
"ecosystems": [
"crates"
],
"dependencies": [
{
"name": "nom",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "8.0"
},
{
"name": "thiserror",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "2.0.11"
},
{
"name": "serde",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "1.0"
},
{
"name": "regex",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "1.10"
},
{
"name": "chrono",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "0.4"
},
{
"name": "tracing",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "0.1.40"
},
{
"name": "tokio",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "1.40.0"
},
{
"name": "bytes",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "1.7.1"
},
{
"name": "iso6709parse",
"manifest": "Cargo.toml",
"ecosystem": "crates",
"version_constraint": "0.1.0"
}
],
"all_dependencies": {
"error": null,
"source": "github-sbom",
"packages": [
{
"name": "bytes",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "chrono",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "iso6709parse",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "nom",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "regex",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "serde",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "thiserror",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "tokio",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "tracing",
"direct": true,
"version": null,
"ecosystem": "crates"
},
{
"name": "afl",
"direct": false,
"version": null,
"ecosystem": "crates"
},
{
"name": "clap",
"direct": false,
"version": null,
"ecosystem": "crates"
},
{
"name": "nom-exif",
"direct": false,
"version": null,
"ecosystem": "crates"
},
{
"name": "rand",
"direct": false,
"version": null,
"ecosystem": "crates"
},
{
"name": "serde_json",
"direct": false,
"version": null,
"ecosystem": "crates"
},
{
"name": "test-case",
"direct": false,
"version": null,
"ecosystem": "crates"
},
{
"name": "tracing-subscriber",
"direct": false,
"version": null,
"ecosystem": "crates"
}
],
"collected": true,
"truncated": false,
"total_count": 16,
"direct_count": 9,
"indirect_count": 7
}
},
"maintainership": {
"issues": {
"open_prs": 2,
"merged_prs": 18,
"open_issues": 3,
"closed_ratio": 0.923,
"closed_issues": 36,
"closed_unmerged_prs": 2
},
"bus_factor": 1,
"bot_contributors": 0,
"top_contributors": [
{
"type": "User",
"login": "mindeng",
"commits": 456,
"avatar_url": "https://avatars.githubusercontent.com/u/921932?v=4"
},
{
"type": "User",
"login": "rolandd",
"commits": 12,
"avatar_url": "https://avatars.githubusercontent.com/u/3670?v=4"
},
{
"type": "User",
"login": "xandkar",
"commits": 4,
"avatar_url": "https://avatars.githubusercontent.com/u/846873?v=4"
},
{
"type": "User",
"login": "onkoe",
"commits": 2,
"avatar_url": "https://avatars.githubusercontent.com/u/66580279?v=4"
},
{
"type": "User",
"login": "Enduriel",
"commits": 2,
"avatar_url": "https://avatars.githubusercontent.com/u/50383024?v=4"
},
{
"type": "User",
"login": "jayvdb",
"commits": 2,
"avatar_url": "https://avatars.githubusercontent.com/u/15092?v=4"
},
{
"type": "User",
"login": "mqudsi",
"commits": 2,
"avatar_url": "https://avatars.githubusercontent.com/u/606923?v=4"
},
{
"type": "User",
"login": "csarn",
"commits": 1,
"avatar_url": "https://avatars.githubusercontent.com/u/4563314?v=4"
},
{
"type": "User",
"login": "only1zf",
"commits": 1,
"avatar_url": "https://avatars.githubusercontent.com/u/213174?v=4"
},
{
"type": "User",
"login": "MarkusWendorf",
"commits": 1,
"avatar_url": "https://avatars.githubusercontent.com/u/18446907?v=4"
}
],
"contributors_sampled": 11,
"top_contributor_share": 0.942
},
"quality_signals": {
"has_ci": true,
"has_tests": true,
"ci_workflows": [
"lib-release.yml",
"rexiftool-release.yml",
"rust.yml"
],
"has_docs_dir": true,
"linter_configs": [],
"has_editorconfig": false,
"has_linter_config": false,
"has_precommit_config": false
},
"security_signals": {
"lockfiles": [],
"scorecard": {
"checks": [
{
"name": "Binary-Artifacts",
"score": 10,
"reason": "no binaries found in the repo",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
},
{
"name": "Branch-Protection",
"score": 0,
"reason": "branch protection not enabled on development/release branches",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
},
{
"name": "CI-Tests",
"score": 10,
"reason": "5 out of 5 merged PRs checked by a CI test -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
},
{
"name": "CII-Best-Practices",
"score": 0,
"reason": "no effort to earn an OpenSSF best practices badge detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
},
{
"name": "Code-Review",
"score": 1,
"reason": "Found 5/30 approved changesets -- score normalized to 1",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
},
{
"name": "Contributors",
"score": 0,
"reason": "project has 0 contributing companies or organizations -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
},
{
"name": "Dangerous-Workflow",
"score": 10,
"reason": "no dangerous workflow patterns detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
},
{
"name": "Dependency-Update-Tool",
"score": 0,
"reason": "no update tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
},
{
"name": "Fuzzing",
"score": 10,
"reason": "project is fuzzed",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
},
{
"name": "License",
"score": 10,
"reason": "license file detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
},
{
"name": "Maintained",
"score": 10,
"reason": "30 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
},
{
"name": "Packaging",
"score": null,
"reason": "packaging workflow not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
},
{
"name": "Pinned-Dependencies",
"score": 0,
"reason": "dependency not pinned by hash detected -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
},
{
"name": "SAST",
"score": 0,
"reason": "SAST tool is not run on all commits -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
},
{
"name": "Security-Policy",
"score": 0,
"reason": "security policy file not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
},
{
"name": "Signed-Releases",
"score": 0,
"reason": "Project has not signed or included provenance with any releases.",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
},
{
"name": "Token-Permissions",
"score": 0,
"reason": "detected GitHub workflow tokens with excessive permissions",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
},
{
"name": "Vulnerabilities",
"score": 10,
"reason": "0 existing vulnerabilities detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
}
],
"commit": "d0b1412d6f115b256fdbc47ba71475f509186745",
"ran_at": "2026-07-21T20:37:02Z",
"aggregate_score": 4.3,
"scorecard_version": "v5.5.0"
},
"has_codeql_workflow": false,
"has_security_policy": false,
"has_dependabot_config": false
}
},
"config": {
"disabled_metrics": [],
"disabled_categories": [],
"disabled_components": {}
},
"source": {
"url": "https://github.com/mindeng/nom-exif",
"host": "github.com",
"name": "nom-exif",
"owner": "mindeng"
},
"metrics": {
"overall": {
"key": "overall",
"band": "moderate",
"name": "Overall health",
"note": null,
"notes": [],
"value": 64,
"inputs": {
"security": 43,
"vitality": 79,
"community": 52,
"governance": 61,
"engineering": 81
},
"components": []
},
"categories": [
{
"key": "vitality",
"band": "good",
"name": "Vitality",
"value": 79,
"weight": 0.22,
"metrics": [
{
"key": "development_activity",
"band": "good",
"name": "Development activity",
"note": null,
"notes": [],
"value": 72,
"inputs": {
"commits_last_year": 274,
"human_commit_share": 1,
"days_since_last_push": 1,
"active_weeks_last_year": 11
},
"components": [
{
"key": "push_recency",
"name": "Push recency",
"detail": "last push 1 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "push_recency",
"params": {
"days": 1
}
}
],
"max_points": 36
},
{
"key": "commit_cadence",
"name": "Commit cadence",
"detail": "11/52 weeks with commits",
"points": 7.6,
"status": "partial",
"details": [
{
"code": "commit_cadence_weeks",
"params": {
"weeks": 11
}
}
],
"max_points": 36
},
{
"key": "commit_volume",
"name": "Commit volume",
"detail": "274 commits in the last year",
"points": 18,
"status": "met",
"details": [
{
"code": "commits_last_year",
"params": {
"count": 274
}
}
],
"max_points": 18
},
{
"key": "openssf_scorecard_maintained",
"name": "OpenSSF Scorecard: Maintained",
"detail": "30 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "release_discipline",
"band": "excellent",
"name": "Release discipline",
"note": null,
"notes": [],
"value": 90,
"inputs": {
"releases_count": 48,
"latest_release_tag": "nom-exif-v3.6.1",
"releases_from_tags": false,
"days_since_latest_release": 40,
"mean_days_between_releases": 3.4
},
"components": [
{
"key": "ships_releases",
"name": "Ships releases",
"detail": "48 releases published",
"points": 27,
"status": "met",
"details": [
{
"code": "releases_published",
"params": {
"count": 48
}
}
],
"max_points": 27
},
{
"key": "release_recency",
"name": "Release recency",
"detail": "latest release 40 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "release_recency",
"params": {
"days": 40
}
}
],
"max_points": 36
},
{
"key": "release_cadence",
"name": "Release cadence",
"detail": "a release every ~3.4 days",
"points": 27,
"status": "met",
"details": [
{
"code": "release_cadence",
"params": {
"gap": 3.4
}
}
],
"max_points": 27
},
{
"key": "openssf_scorecard_signed_releases",
"name": "OpenSSF Scorecard: Signed-Releases",
"detail": "Project has not signed or included provenance with any releases.",
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
},
{
"key": "abandonment",
"band": "excellent",
"name": "Abandonment",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"cap": null,
"state": "maintained",
"guards": [],
"signals": [],
"red_flag": false,
"multiplier_pct": 100,
"declared_reason": null,
"unverified_reason": null,
"unanswered_open_prs": null,
"unanswered_open_issues": null,
"days_since_last_merged_pr": null,
"days_since_last_human_commit": 40,
"days_since_last_human_commit_is_floor": false
},
"components": [
{
"key": "project_is_still_maintained",
"name": "Project is still maintained",
"detail": "last human commit 40 days ago",
"points": 100,
"status": "met",
"details": [
{
"code": "abandonment_maintained",
"params": {
"days": 40
}
}
],
"max_points": 100
}
]
}
],
"description": "Is the project alive — is code being written and are releases shipping?"
},
{
"key": "community",
"band": "moderate",
"name": "Community & Adoption",
"value": 52,
"weight": 0.18,
"metrics": [
{
"key": "popularity",
"band": "at_risk",
"name": "Popularity & adoption",
"note": null,
"notes": [],
"value": 43,
"inputs": {
"forks": 16,
"stars": 117,
"watchers": 2,
"growth_state": "organic",
"growth_factor_pct": 100
},
"components": [
{
"key": "stars",
"name": "Stars",
"detail": "117 stars",
"points": 33.5,
"status": "partial",
"details": [
{
"code": "stars",
"params": {
"count": 117
}
}
],
"max_points": 60
},
{
"key": "forks",
"name": "Forks",
"detail": "16 forks",
"points": 9.8,
"status": "partial",
"details": [
{
"code": "forks",
"params": {
"count": 16
}
}
],
"max_points": 25
},
{
"key": "watchers",
"name": "Watchers",
"detail": "2 watchers",
"points": 0,
"status": "missed",
"details": [
{
"code": "watchers",
"params": {
"count": 2
}
}
],
"max_points": 15
}
]
},
{
"key": "community_health",
"band": "moderate",
"name": "Community health",
"note": null,
"notes": [],
"value": 50,
"inputs": {
"has_readme": true,
"has_license": true,
"has_contributing": false,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 22.5,
"status": "met",
"details": [],
"max_points": 22.5
},
{
"key": "license",
"name": "License",
"detail": "recognized license (MIT)",
"points": 22.5,
"status": "met",
"details": [
{
"code": "license_standard",
"params": {}
},
{
"code": "license_spdx",
"params": {
"spdx": "MIT"
}
}
],
"max_points": 22.5
},
{
"key": "contributing_guide",
"name": "CONTRIBUTING guide",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 18
},
{
"key": "code_of_conduct",
"name": "Code of conduct",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 13.5
},
{
"key": "issue_template",
"name": "Issue template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.2
},
{
"key": "pr_template",
"name": "PR template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.3
}
]
},
{
"key": "ecosystem_adoption",
"band": "moderate",
"name": "Ecosystem adoption (downloads)",
"note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"registry_dependents"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 68,
"inputs": {
"packages": [
"nom-exif"
],
"dependents": null,
"ecosystems": "crates",
"total_downloads": 155187,
"monthly_downloads": 12569
},
"components": [
{
"key": "monthly_downloads",
"name": "Monthly downloads",
"detail": "12,569 downloads/month across crates",
"points": 54.7,
"status": "partial",
"details": [
{
"code": "downloads_monthly",
"params": {
"count": 12569,
"ecosystems": "crates"
}
}
],
"max_points": 80
},
{
"key": "registry_dependents",
"name": "Registry dependents",
"detail": "not reported by this ecosystem",
"points": 0,
"status": "excluded",
"details": [
{
"code": "not_reported_by_this_ecosystem",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
},
{
"key": "governance",
"band": "moderate",
"name": "Sustainability & Governance",
"value": 61,
"weight": 0.24,
"metrics": [
{
"key": "maintainer_resilience",
"band": "critical",
"name": "Maintainer resilience (bus factor)",
"note": null,
"notes": [],
"value": 24,
"inputs": {
"bus_factor": 1,
"contributors_sampled": 11,
"top_contributor_share": 0.942
},
"components": [
{
"key": "bus_factor",
"name": "Bus factor",
"detail": "1 contributor(s) cover half of all commits",
"points": 9,
"status": "partial",
"details": [
{
"code": "bus_factor",
"params": {
"count": 1
}
}
],
"max_points": 54
},
{
"key": "commit_distribution",
"name": "Commit distribution",
"detail": "top contributor authored 94% of commits",
"points": 1.3,
"status": "partial",
"details": [
{
"code": "top_contributor_share",
"params": {
"share": 94
}
}
],
"max_points": 22.5
},
{
"key": "contributor_breadth",
"name": "Contributor breadth",
"detail": "11 contributors",
"points": 13.5,
"status": "met",
"details": [
{
"code": "contributors_sampled",
"params": {
"count": 11
}
}
],
"max_points": 13.5
},
{
"key": "openssf_scorecard_contributors",
"name": "OpenSSF Scorecard: Contributors",
"detail": "project has 0 contributing companies or organizations -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
},
{
"key": "responsiveness",
"band": "good",
"name": "Issue & PR responsiveness",
"note": null,
"notes": [],
"value": 79,
"inputs": {
"merged_prs": 18,
"open_issues": 3,
"closed_issues": 36,
"issue_closed_ratio": 0.923,
"closed_unmerged_prs": 2
},
"components": [
{
"key": "issue_resolution",
"name": "Issue resolution",
"detail": "92% of issues closed",
"points": 43.2,
"status": "partial",
"details": [
{
"code": "issues_closed_share",
"params": {
"share": 92
}
}
],
"max_points": 46.75
},
{
"key": "pr_acceptance",
"name": "PR acceptance",
"detail": "18/20 decided PRs merged",
"points": 34.4,
"status": "partial",
"details": [
{
"code": "decided_prs_merged",
"params": {
"merged": 18,
"decided": 20
}
}
],
"max_points": 38.25
},
{
"key": "openssf_scorecard_code_review",
"name": "OpenSSF Scorecard: Code-Review",
"detail": "Found 5/30 approved changesets -- score normalized to 1",
"points": 1.5,
"status": "partial",
"details": [],
"max_points": 15
}
]
},
{
"key": "stewardship",
"band": "moderate",
"name": "Ownership & stewardship",
"note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"verified_domain"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 58,
"inputs": {
"followers": 39,
"owner_type": "User",
"is_verified": null,
"owner_login": "mindeng",
"public_repos": 70,
"account_age_days": 5482
},
"components": [
{
"key": "ownership_backing",
"name": "Ownership backing",
"detail": "personal (user) account",
"points": 10,
"status": "partial",
"details": [
{
"code": "owner_personal",
"params": {}
}
],
"max_points": 30
},
{
"key": "verified_domain",
"name": "Verified domain",
"detail": "not applicable to user accounts",
"points": 0,
"status": "excluded",
"details": [
{
"code": "not_applicable_to_user_accounts",
"params": {}
}
],
"max_points": 20
},
{
"key": "owner_reach",
"name": "Owner reach",
"detail": "39 followers of mindeng",
"points": 11.5,
"status": "partial",
"details": [
{
"code": "owner_followers",
"params": {
"count": 39,
"login": "mindeng"
}
}
],
"max_points": 25
},
{
"key": "track_record",
"name": "Track record",
"detail": "70 public repos, account ~15 yr old",
"points": 25,
"status": "met",
"details": [
{
"code": "public_repos",
"params": {
"count": 70
}
},
{
"code": "account_age_years",
"params": {
"years": 15
}
}
],
"max_points": 25
}
]
},
{
"key": "package_maintenance",
"band": "excellent",
"name": "Package maintenance",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"packages": [
"nom-exif"
],
"ecosystems": "crates",
"any_deprecated": false,
"min_days_since_publish": 1
},
"components": [
{
"key": "published_resolvable",
"name": "Published & resolvable",
"detail": "1 package(s) on crates",
"points": 25,
"status": "met",
"details": [
{
"code": "packages_published",
"params": {
"count": 1,
"ecosystems": "crates"
}
}
],
"max_points": 25
},
{
"key": "publish_recency",
"name": "Publish recency",
"detail": "latest publish 1 days ago",
"points": 35,
"status": "met",
"details": [
{
"code": "publish_recency",
"params": {
"days": 1
}
}
],
"max_points": 35
},
{
"key": "version_history",
"name": "Version history",
"detail": "55 published versions",
"points": 20,
"status": "met",
"details": [
{
"code": "published_versions",
"params": {
"count": 55
}
}
],
"max_points": 20
},
{
"key": "not_deprecated",
"name": "Not deprecated",
"detail": "active, not deprecated or yanked",
"points": 20,
"status": "met",
"details": [
{
"code": "package_not_deprecated",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
},
{
"key": "engineering",
"band": "good",
"name": "Engineering Quality",
"value": 81,
"weight": 0.2,
"metrics": [
{
"key": "engineering_practices",
"band": "moderate",
"name": "Engineering practices",
"note": null,
"notes": [],
"value": 68,
"inputs": {
"has_ci": true,
"has_tests": true,
"has_editorconfig": false,
"has_linter_config": false,
"has_precommit_config": false
},
"components": [
{
"key": "ci_workflows",
"name": "CI workflows",
"detail": "3 workflow(s)",
"points": 24,
"status": "met",
"details": [
{
"code": "ci_workflows",
"params": {
"count": 3
}
}
],
"max_points": 24
},
{
"key": "tests_present",
"name": "Tests present",
"detail": null,
"points": 24,
"status": "met",
"details": [],
"max_points": 24
},
{
"key": "linter_config",
"name": "Linter config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 16
},
{
"key": "pre_commit_hooks",
"name": "Pre-commit hooks",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 9.6
},
{
"key": "editorconfig",
"name": ".editorconfig",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.4
},
{
"key": "openssf_scorecard_ci_tests",
"name": "OpenSSF Scorecard: CI-Tests",
"detail": "5 out of 5 merged PRs checked by a CI test -- score normalized to 10",
"points": 20,
"status": "met",
"details": [],
"max_points": 20
}
]
},
{
"key": "documentation",
"band": "excellent",
"name": "Documentation",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"topics": [
"exif",
"heif",
"jpeg",
"metadata",
"mp4",
"nom",
"parser",
"quicktime",
"rust",
"matroska",
"mkv",
"webm",
"mov",
"raw",
"tiff",
"cr3"
],
"has_wiki": true,
"homepage": "https://crates.io/crates/nom-exif",
"has_readme": true,
"has_docs_dir": true,
"has_description": true
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 30,
"status": "met",
"details": [],
"max_points": 30
},
{
"key": "documentation_directory",
"name": "Documentation directory",
"detail": null,
"points": 25,
"status": "met",
"details": [],
"max_points": 25
},
{
"key": "documentation_homepage_site",
"name": "Documentation / homepage site",
"detail": "https://crates.io/crates/nom-exif",
"points": 15,
"status": "met",
"details": [],
"max_points": 15
},
{
"key": "repository_description",
"name": "Repository description",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "topics",
"name": "Topics",
"detail": "16 topics",
"points": 10,
"status": "met",
"details": [
{
"code": "topics_count",
"params": {
"count": 16
}
}
],
"max_points": 10
},
{
"key": "wiki",
"name": "Wiki",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
}
],
"description": "Are baseline engineering and documentation practices in place?"
},
{
"key": "security",
"band": "at_risk",
"name": "Security",
"value": 43,
"weight": 0.16,
"metrics": [
{
"key": "security_posture",
"band": "at_risk",
"name": "Security posture",
"note": "Excluded from scoring (no data or not applicable): Packaging. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"packaging"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 43,
"inputs": {
"source": "openssf_scorecard",
"checks_evaluated": 17,
"scorecard_version": "v5.5.0",
"checks_inconclusive": 1,
"scorecard_aggregate": 4.3
},
"components": [
{
"key": "binary_artifacts",
"name": "Binary-Artifacts",
"detail": "no binaries found in the repo",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "branch_protection",
"name": "Branch-Protection",
"detail": "branch protection not enabled on development/release branches",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "ci_tests",
"name": "CI-Tests",
"detail": "5 out of 5 merged PRs checked by a CI test -- score normalized to 10",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "cii_best_practices",
"name": "CII-Best-Practices",
"detail": "no effort to earn an OpenSSF best practices badge detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "code_review",
"name": "Code-Review",
"detail": "Found 5/30 approved changesets -- score normalized to 1",
"points": 0.8,
"status": "partial",
"details": [],
"max_points": 7.5
},
{
"key": "contributors",
"name": "Contributors",
"detail": "project has 0 contributing companies or organizations -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "dangerous_workflow",
"name": "Dangerous-Workflow",
"detail": "no dangerous workflow patterns detected",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "dependency_update_tool",
"name": "Dependency-Update-Tool",
"detail": "no update tool detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "fuzzing",
"name": "Fuzzing",
"detail": "project is fuzzed",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "license",
"name": "License",
"detail": "license file detected",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "maintained",
"name": "Maintained",
"detail": "30 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "packaging",
"name": "Packaging",
"detail": "packaging workflow not detected",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 5
},
{
"key": "pinned_dependencies",
"name": "Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "sast",
"name": "SAST",
"detail": "SAST tool is not run on all commits -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "security_policy",
"name": "Security-Policy",
"detail": "security policy file not detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "signed_releases",
"name": "Signed-Releases",
"detail": "Project has not signed or included provenance with any releases.",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "token_permissions",
"name": "Token-Permissions",
"detail": "detected GitHub workflow tokens with excessive permissions",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "vulnerabilities",
"name": "Vulnerabilities",
"detail": "0 existing vulnerabilities detected",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
}
]
},
{
"key": "high_risk_jurisdiction_exposure",
"band": "excellent",
"name": "High-Risk Jurisdiction Exposure",
"note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
"notes": [
{
"code": "jurisdiction_evidence_limits",
"params": {}
}
],
"value": 100,
"inputs": {
"meaning": "self-published location evidence; not nationality or citizenship",
"red_flag": false,
"exposures": [],
"policy_countries": [
"Russia",
"Iran",
"North Korea"
],
"review_only_matches": 0,
"assessed_self_published_locations": 21
},
"components": [
{
"key": "policy_exposure_multiplier",
"name": "Policy exposure multiplier",
"detail": "no confirmed policy-scope location match",
"points": 100,
"status": "met",
"details": [
{
"code": "jurisdiction_no_match",
"params": {}
}
],
"max_points": 100
}
]
}
],
"description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
},
{
"key": "ai_readiness",
"band": "moderate",
"name": "AI Readiness",
"value": 53,
"weight": 0,
"metrics": [
{
"key": "ai_agent_context",
"band": "at_risk",
"name": "Agent context & guidance",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"has_llms_txt": false,
"legible_history_share": 0.98,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"components": [
{
"key": "agent_instructions",
"name": "Agent instructions",
"detail": "no CLAUDE.md / AGENTS.md / editor rules",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_instructions",
"params": {}
}
],
"max_points": 45
},
{
"key": "machine_readable_docs_llms_txt",
"name": "Machine-readable docs (llms.txt)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "legible_commit_history",
"name": "Legible commit history",
"detail": "98 of 100 human commits state their intent (structured subject or explanatory body)",
"points": 40,
"status": "met",
"details": [
{
"code": "legible_history",
"params": {
"legible": 98,
"sampled": 100
}
}
],
"max_points": 40
}
]
},
{
"key": "ai_verify_loop",
"band": "at_risk",
"name": "Verify loop (build / test / typecheck)",
"note": null,
"notes": [],
"value": 46,
"inputs": {
"has_nix": false,
"has_tests": true,
"lockfiles": [],
"has_dockerfile": false,
"typed_language": true,
"bootstrap_files": [],
"has_devcontainer": false,
"has_linter_config": false,
"typecheck_configs": [],
"agent_commit_share": 0,
"toolchain_manifests": [
"Cargo.toml",
"crates/rexiftool/Cargo.toml",
"fuzz/Cargo.toml"
],
"dependency_bot_commit_share": 0
},
"components": [
{
"key": "one_command_bootstrap",
"name": "One-command bootstrap",
"detail": "Cargo.toml, crates/rexiftool/Cargo.toml, fuzz/Cargo.toml (toolchain convention, no task runner)",
"points": 12.6,
"status": "partial",
"details": [
{
"code": "toolchain_convention",
"params": {
"files": "Cargo.toml, crates/rexiftool/Cargo.toml, fuzz/Cargo.toml"
}
}
],
"max_points": 18
},
{
"key": "automated_tests",
"name": "Automated tests",
"detail": null,
"points": 22,
"status": "met",
"details": [],
"max_points": 22
},
{
"key": "lint_format_config",
"name": "Lint / format config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "static_type_checking",
"name": "Static type checking",
"detail": "Rust (statically typed)",
"points": 11,
"status": "met",
"details": [
{
"code": "statically_typed_language",
"params": {
"language": "Rust"
}
}
],
"max_points": 11
},
{
"key": "reproducible_environment",
"name": "Reproducible environment",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "demonstrated_agent_practice",
"name": "Demonstrated agent practice",
"detail": "no agent-authored commits among the last 100",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_authored_commits",
"params": {
"sampled": 100
}
}
],
"max_points": 10
},
{
"key": "automated_maintenance",
"name": "Automated maintenance",
"detail": "no automated dependency updates observed",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_dependency_automation",
"params": {}
}
],
"max_points": 8
},
{
"key": "openssf_scorecard_pinned_dependencies",
"name": "OpenSSF Scorecard: Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
},
{
"key": "ai_code_legibility",
"band": "excellent",
"name": "Code legibility for models",
"note": null,
"notes": [],
"value": 99,
"inputs": {
"primary_language": "Rust",
"largest_source_bytes": 87944,
"source_files_sampled": 45,
"oversized_source_files": 1
},
"components": [
{
"key": "type_checkable_code",
"name": "Type-checkable code",
"detail": "Rust (statically typed)",
"points": 45,
"status": "met",
"details": [
{
"code": "statically_typed_language",
"params": {
"language": "Rust"
}
}
],
"max_points": 45
},
{
"key": "manageable_file_sizes",
"name": "Manageable file sizes",
"detail": "1/45 source files over 60KB",
"points": 53.8,
"status": "partial",
"details": [
{
"code": "oversized_source_files",
"params": {
"kb": 60,
"sampled": 45,
"oversized": 1
}
}
],
"max_points": 55
}
]
}
],
"description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
}
],
"metrics_version": "1.13.0"
},
"warnings": [
"No resolved dependencies carried a version and a supported ecosystem"
],
"report_type": "repository",
"generated_at": "2026-07-21T20:37:19.007077Z",
"schema_version": "0.23.0",
"badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/m/mindeng/nom-exif.svg",
"full_name": "mindeng/nom-exif",
"license_state": "standard",
"license_spdx": "MIT"
}