diff --git a/.changeset/modern-breads-warn.md b/.changeset/modern-breads-warn.md new file mode 100644 index 0000000..3b61a8f --- /dev/null +++ b/.changeset/modern-breads-warn.md @@ -0,0 +1,5 @@ +--- +"@contentauth/c2pa-node": patch +--- + +Add method for adding redactions to builder diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 416a6b3..36505d8 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -44,6 +44,21 @@ jobs: SKIP_RUST_BUILD: 1 run: pnpm install --frozen-lockfile + - name: Cache Trustmark models + id: cache-trustmark-models + uses: actions/cache@v4 + with: + path: tmp/trustmark_models + key: trustmark-models-B-v1 + + - name: Download Trustmark models + if: steps.cache-trustmark-models.outputs.cache-hit != 'true' + run: | + mkdir -p tmp/trustmark_models + BASE="https://cai-watermark.adobe.net/watermarking/trustmark-models" + curl -fSL "$BASE/encoder_B.onnx" -o tmp/trustmark_models/encoder_B.onnx + curl -fSL "$BASE/decoder_B.onnx" -o tmp/trustmark_models/decoder_B.onnx + - run: pnpm run ci - run: pnpm run lint diff --git a/Cargo.lock b/Cargo.lock index 96c44e3..c84d3f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,7 +213,7 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" dependencies = [ - "brotli", + "brotli 8.0.1", "flate2", "futures-core", "memchr", @@ -257,9 +257,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atree" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132573478eb9ff973c6f75d0ed425ac12da77d266506483345f46743ecc83a98" +checksum = "239d25181cb40f1955529367ee495e35d03aab4e578028f41e7abc8b21c367a8" [[package]] name = "autocfg" @@ -397,6 +397,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 4.0.3", +] + [[package]] name = "brotli" version = "8.0.1" @@ -405,7 +416,17 @@ checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", + "brotli-decompressor 5.0.0", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", ] [[package]] @@ -495,9 +516,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "c2pa" -version = "0.78.4" +version = "0.82.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cae87cdb2ae6070bf628f889d745797071b194ecb31c711b452617308075c5e" +checksum = "8e96917bbbabdb4dd15030b67e4694b6751d618768fe88ad57f98aad7fd8e751" dependencies = [ "asn1-rs", "async-generic", @@ -505,6 +526,7 @@ dependencies = [ "atree", "base64", "bcder", + "brotli 7.0.0", "byteorder", "byteordered", "bytes", @@ -522,6 +544,7 @@ dependencies = [ "extfmt", "getrandom 0.2.16", "getrandom 0.3.4", + "glob", "hex", "hex-literal", "http", @@ -1426,6 +1449,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "group" version = "0.13.0" diff --git a/Cargo.toml b/Cargo.toml index 3603020..e9523ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] [dependencies] async-trait = "0.1.77" ciborium = "0.2.2" -c2pa = { version = "0.78.4", default-features = false, features = ["file_io", "pdf", "fetch_remote_manifests", "add_thumbnails", "rust_native_crypto", "default_http"] } +c2pa = { version = "0.82.1", default-features = false, features = ["file_io", "pdf", "fetch_remote_manifests", "add_thumbnails", "rust_native_crypto", "default_http"] } futures = "0.3" image = "0.25.6" neon = { version = "1.0.0", default-features = false, features = [ diff --git a/js-src/Builder.spec.ts b/js-src/Builder.spec.ts index 8dfb090..7a77a5d 100644 --- a/js-src/Builder.spec.ts +++ b/js-src/Builder.spec.ts @@ -571,10 +571,10 @@ describe("Builder", () => { customBool: true, customObject: { nested: "value", - count: 123 + count: 123, }, - customArray: ["item1", "item2", "item3"] - } + customArray: ["item1", "item2", "item3"], + }, }; await builder.addIngredient(JSON.stringify(ingredient)); @@ -583,57 +583,72 @@ describe("Builder", () => { expect(definition.ingredients![0]).toMatchObject(ingredient); }); - it("should perform redaction workflow like test_redaction_async", async () => { - // This test mirrors the Rust test_redaction_async test - - // Create a reader to get the parent manifest label from the existing source - const reader = await Reader.fromAsset(source); - expect(reader).not.toBeNull(); - const parentManifestLabel = reader!.activeLabel(); - expect(parentManifestLabel).toBeDefined(); - - // Create a redacted URI for the assertion we are going to redact - // Using a common assertion label that might exist - const assertionLabel = "stds.schema-org.CreativeWork"; - const redactedUri = `contentauth:urn:uuid:${parentManifestLabel}/c2pa.assertions/${assertionLabel}`; + it("should redact a thumbnail from an ingredient manifest", async () => { + const signerConfig: JsCallbackSignerConfig = { + alg: "es256", + certs: [publicKey], + reserveSize: 10000, + tsaUrl: undefined, + directCoseHandling: false, + }; + const testSigner = new TestSigner(privateKey); + const signer = CallbackSigner.newSigner(signerConfig, testSigner.sign); - // Create a builder with edit intent and redactions - const redactionManifestDefinition = { - claim_generator: "test-generator", - claim_generator_info: [ - { - name: "c2pa_test", - version: "1.0.0", - }, - ], - title: "Test_Redaction_Manifest", + // Sign source asset with a thumbnail + const step1Builder = Builder.withJson({ + claim_generator_info: [{ name: "c2pa_test", version: "1.0.0" }], + title: "Asset With Thumbnail", format: "image/jpeg", - instance_id: "1234", - intent: "edit", - redactions: [redactedUri], - assertions: [ - { - label: "org.test.assertion", - data: {}, - }, - ], - resources: { resources: {} }, + instance_id: "thumb-step1-1234", + thumbnail: { format: "image/jpeg", identifier: "thumbnail.jpg" }, + }); + await step1Builder.addResource("thumbnail.jpg", { + mimeType: "image/jpeg", + buffer: testThumbnail, + }); + const step1Dest = { buffer: null }; + await step1Builder.signAsync(signer, source, step1Dest); + const step1Asset = { + buffer: step1Dest.buffer! as Buffer, + mimeType: "image/jpeg", }; - const builder = Builder.withJson(redactionManifestDefinition); + // Verify thumbnail exists in original manifest + const originalReader = await Reader.fromAsset(step1Asset); + expect(originalReader).not.toBeNull(); + const parentLabel = originalReader!.activeLabel(); + expect(parentLabel).toBeDefined(); + const originalManifest = originalReader!.getActive(); + expect(originalManifest?.thumbnail).toBeDefined(); + expect(originalManifest?.thumbnail).not.toBeNull(); + + // Redact the thumbnail + const thumbnailUri = `self#jumbf=/c2pa/${parentLabel}/c2pa.assertions/c2pa.thumbnail.claim`; + const redactionBuilder = Builder.withJson({ + claim_generator_info: [{ name: "c2pa_test", version: "1.0.0" }], + title: "Redacted Thumbnail Manifest", + format: "image/jpeg", + instance_id: "thumb-step2-1234", + }); + redactionBuilder.setIntent("update"); + redactionBuilder.addRedaction(thumbnailUri, "c2pa.PII.present"); - // Add a redacted action - const redactedAction = { - actions: [ - { - action: "c2pa.redacted", - }, - ], - }; + const step2Dest = { buffer: null }; + await redactionBuilder.signAsync(signer, step1Asset, step2Dest); - builder.addAssertion("c2pa.actions", redactedAction, "Cbor"); + const finalReader = await Reader.fromAsset({ + buffer: step2Dest.buffer! as Buffer, + mimeType: "image/jpeg", + }); + expect(finalReader).not.toBeNull(); + + const store = finalReader!.json(); + const parentManifest = store.manifests?.[parentLabel!]; + expect(parentManifest).toBeDefined(); + expect(parentManifest?.thumbnail).toBeUndefined(); + }); - // Use the callback signer like the other test + it("should redact an assertion from an ingredient manifest", async () => { const signerConfig: JsCallbackSignerConfig = { alg: "es256", certs: [publicKey], @@ -644,38 +659,90 @@ describe("Builder", () => { const testSigner = new TestSigner(privateKey); const signer = CallbackSigner.newSigner(signerConfig, testSigner.sign); - // Sign the manifest with the original image as input - const dest = { buffer: null }; - const outputBuffer = await builder.signAsync(signer, source, dest); - expect(outputBuffer.length).toBeGreaterThan(0); + // Sign source asset with multiple distinct assertions + const piiLabel = "stds.schema-org.CreativeWork"; + const retainedLabel = "org.contentauth.retained"; + const step1Builder = Builder.withJson({ + claim_generator_info: [{ name: "c2pa_test", version: "1.0.0" }], + title: "Asset With Multiple Assertions", + format: "image/jpeg", + instance_id: "assert-step1-1234", + assertions: [ + { + label: piiLabel, + data: { + "@context": "http://schema.org/", + "@type": "CreativeWork", + author: [{ "@type": "Person", name: "John Doe" }], + }, + }, + { + label: retainedLabel, + data: { keep: true }, + }, + ], + }); + const step1Dest = { buffer: null }; + await step1Builder.signAsync(signer, source, step1Dest); + const step1Asset = { + buffer: step1Dest.buffer! as Buffer, + mimeType: "image/jpeg", + }; - // Verify the result by reading the signed manifest - const signedReader = await Reader.fromAsset({ - buffer: dest.buffer! as Buffer, + // Verify both assertions exist in original manifest + const originalReader = await Reader.fromAsset(step1Asset); + expect(originalReader).not.toBeNull(); + const parentLabel = originalReader!.activeLabel(); + expect(parentLabel).toBeDefined(); + + const originalStore = originalReader!.json(); + const originalLabels = originalStore.manifests![parentLabel!].assertions!.map( + (a: any) => a.label, + ); + expect(originalLabels).toContain(piiLabel); + expect(originalLabels).toContain(retainedLabel); + + // Redact only the PII assertion + const redactionUri = `self#jumbf=/c2pa/${parentLabel}/c2pa.assertions/${piiLabel}`; + const redactionBuilder = Builder.withJson({ + claim_generator_info: [{ name: "c2pa_test", version: "1.0.0" }], + title: "Redacted Assertion Manifest", + format: "image/jpeg", + instance_id: "assert-step2-1234", + }); + redactionBuilder.setIntent("update"); + redactionBuilder.addRedaction(redactionUri, "c2pa.PII.present"); + + const step2Dest = { buffer: null }; + await redactionBuilder.signAsync(signer, step1Asset, step2Dest); + + const finalReader = await Reader.fromAsset({ + buffer: step2Dest.buffer! as Buffer, mimeType: "image/jpeg", }); - expect(signedReader).not.toBeNull(); - expect(signedReader).toBeDefined(); + expect(finalReader).not.toBeNull(); - // Check that the manifest was created successfully - const activeManifest = signedReader!.getActive(); - expect(activeManifest).toBeDefined(); + const store = finalReader!.json(); + const parentManifest = store.manifests?.[parentLabel!]; + expect(parentManifest).toBeDefined(); - // Verify the redacted action was added - const assertions = activeManifest?.assertions; - const actionsAssertion = assertions?.find( - (a: any) => a.label === "c2pa.actions.v2", + const assertionLabels = parentManifest?.assertions?.map( + (a: any) => a.label, ); - expect(actionsAssertion).toBeDefined(); + // PII assertion removed, retained assertion still present + expect(assertionLabels).not.toContain(piiLabel); + expect(assertionLabels).toContain(retainedLabel); + }); - if (actionsAssertion && isActionsAssertion(actionsAssertion)) { - const actions = actionsAssertion.data.actions; - const redactedAction = actions.find( - (a: any) => a.action === "c2pa.redacted", - ); - expect(redactedAction).toBeDefined(); - expect(redactedAction?.action).toBe("c2pa.redacted"); - } + it("should add redactions via addRedaction method", () => { + const uri1 = "self#jumbf=/c2pa/test-label/c2pa.assertions/cawg.identity"; + const uri2 = + "self#jumbf=/c2pa/test-label/c2pa.assertions/stds.schema-org.CreativeWork"; + const builder = Builder.new(); + builder.addRedaction(uri1, "c2pa.PII.present"); + builder.addRedaction(uri2, "c2pa.PII.present"); + const definition = builder.getManifestDefinition(); + expect(definition.redactions).toEqual([uri1, uri2]); }); it("should test builder remote url", async () => { diff --git a/js-src/Builder.ts b/js-src/Builder.ts index 039c76d..366ecbc 100644 --- a/js-src/Builder.ts +++ b/js-src/Builder.ts @@ -13,6 +13,7 @@ import type { BuilderIntent, + C2paReason, Ingredient, Manifest, } from "@contentauth/c2pa-types"; @@ -259,6 +260,10 @@ export class Builder implements BuilderInterface { ); } + addRedaction(uri: string, reason: C2paReason): void { + getNeonBinary().builderAddRedaction.call(this.builder, uri, reason); + } + getHandle(): NeonBuilderHandle { return this.builder; } diff --git a/js-src/Reader.spec.ts b/js-src/Reader.spec.ts index 4ad3800..37a7979 100644 --- a/js-src/Reader.spec.ts +++ b/js-src/Reader.spec.ts @@ -53,7 +53,7 @@ describe("Reader", () => { "instance_id": "xmp.iid:813ee422-9736-4cdc-9be6-4e35ed8e41cb", "thumbnail": { "format": "image/jpeg", - "identifier": "self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg" + "identifier": "self#jumbf=/c2pa/contentauth:urn:uuid:c2677d4b-0a93-4444-876f-ed2f2d40b8cf/c2pa.assertions/c2pa.thumbnail.ingredient.jpeg" }, "relationship": "parentOf", "label": "c2pa.ingredient" diff --git a/js-src/index.node.d.ts b/js-src/index.node.d.ts index ccb6426..d3660b9 100644 --- a/js-src/index.node.d.ts +++ b/js-src/index.node.d.ts @@ -14,6 +14,7 @@ import { Buffer } from "buffer"; import type { + C2paReason, CallbackSignerConfig, ClaimVersion, DestinationAsset, @@ -85,6 +86,7 @@ declare module "index.node" { property: string, value: string | ClaimVersion, ): void; + export function builderAddRedaction(uri: string, reason: C2paReason): void; // Reader methods export function readerFromAsset( diff --git a/js-src/types.d.ts b/js-src/types.d.ts index c82e9fb..1f63098 100644 --- a/js-src/types.d.ts +++ b/js-src/types.d.ts @@ -15,12 +15,13 @@ import { Buffer } from "buffer"; import type { BuilderIntent, + C2paReason, Ingredient, Manifest, ManifestStore, } from "@contentauth/c2pa-types"; -export type { Ingredient } from "@contentauth/c2pa-types"; +export type { C2paReason, Ingredient } from "@contentauth/c2pa-types"; /** * Describes the digital signature algorithms allowed by the C2PA spec @@ -332,6 +333,15 @@ export interface BuilderInterface { */ updateManifestProperty(property: string, value: string | ClaimVersion): void; + /** + * Redact an assertion from an ingredient manifest and record the reason. + * Adds the URI to `definition.redactions` and appends a `c2pa.redacted` action + * with `parameters.redacted` pointing to the same URI. + * @param uri JUMBF URI of the assertion to redact (e.g. `self#jumbf=/c2pa/{label}/c2pa.assertions/{name}`) + * @param reason Why the assertion is being redacted. Use `"c2pa.PII.present"` for PII removal. + */ + addRedaction(uri: string, reason: C2paReason): void; + /** * Get the internal handle for use with Neon bindings */ diff --git a/package.json b/package.json index dfc5773..3436817 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "license": "MIT", "devDependencies": { "@changesets/cli": "^2.31.0", - "@contentauth/c2pa-types": "^0.4.3", + "@contentauth/c2pa-types": "^0.4.4", "@eslint/js": "^9.39.4", "@neon-rs/cli": "0.1.82", "@types/cli-progress": "^3.11.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4695f58..1809d6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,8 +40,8 @@ importers: specifier: ^2.31.0 version: 2.31.0(@types/node@22.19.17) '@contentauth/c2pa-types': - specifier: ^0.4.3 - version: 0.4.3 + specifier: ^0.4.4 + version: 0.4.4 '@eslint/js': specifier: ^9.39.4 version: 9.39.4 @@ -385,8 +385,8 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@contentauth/c2pa-types@0.4.3': - resolution: {integrity: sha512-uJJPpv8kS/wLaokb4lPGycyK9yf663H4Db/QDkdWuxxVM8d0UR4ZaIrJXWBeYO9M8gqH8JYJA/WBRkchboJWSQ==} + '@contentauth/c2pa-types@0.4.4': + resolution: {integrity: sha512-g6Ryna1iK8k+cU3rkDuVb/SHUmrVAXWWgZsy4ByVNT1yRnp9DeyC67VwS+9pIsgLljVbijm4vxS3pU68hnricw==} '@cto.af/wtf8@0.0.5': resolution: {integrity: sha512-LfUFi+Vv4eDzj+XAtR89e3wwjXA/NZjUSwU5NhwbBrLecxPaBYFy3exCuc1j+D4UZeOVdqlsl8G7LmOt18V0tg==} @@ -3709,7 +3709,7 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 - '@contentauth/c2pa-types@0.4.3': {} + '@contentauth/c2pa-types@0.4.4': {} '@cto.af/wtf8@0.0.5': {} diff --git a/src/lib.rs b/src/lib.rs index 438d407..17de09f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,6 +81,10 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> { "builderUpdateManifestProperty", neon_builder::NeonBuilder::update_manifest_property, )?; + cx.export_function( + "builderAddRedaction", + neon_builder::NeonBuilder::add_redaction, + )?; // Reader cx.export_function("readerNew", neon_reader::NeonReader::new)?; diff --git a/src/neon_builder.rs b/src/neon_builder.rs index de1236d..70f0fb4 100644 --- a/src/neon_builder.rs +++ b/src/neon_builder.rs @@ -18,7 +18,7 @@ use crate::neon_reader::NeonReader; use crate::neon_signer::{CallbackSignerConfig, NeonCallbackSigner, NeonLocalSigner}; use crate::runtime::runtime; use crate::utils::parse_settings; -use c2pa::{Builder, BuilderIntent, Ingredient}; +use c2pa::{assertions::Action, Builder, BuilderIntent, Ingredient}; use neon::context::Context as NeonContext; use neon::prelude::*; use neon_serde4; @@ -60,7 +60,9 @@ impl NeonBuilder { .with_definition(json.as_str()) .or_else(|err| cx.throw_error(err.to_string()))? } else { - Builder::from_json(&json).or_else(|err| cx.throw_error(err.to_string()))? + Builder::default() + .with_definition(&json) + .or_else(|err| cx.throw_error(err.to_string()))? }; Ok(cx.boxed(Self { @@ -73,7 +75,7 @@ impl NeonBuilder { let this = cx.this::>()?; let intent_str = cx.argument::(0)?.value(&mut cx); let intent: BuilderIntent = serde_json::from_str(&intent_str) - .or_else(|_| cx.throw_error(format!("Invalid intent: {}", intent_str)))?; + .or_else(|_| cx.throw_error(format!("Invalid intent: {intent_str}")))?; let mut builder = rt.block_on(async { this.builder.lock().await }); builder.set_intent(intent); Ok(cx.undefined()) @@ -127,7 +129,7 @@ impl NeonBuilder { // For Json, expect the assertion as a string (JSON) and parse it let assertion_str = cx.argument::(1)?.value(&mut cx); let assertion: serde_json::Value = serde_json::from_str(&assertion_str) - .or_else(|err| cx.throw_error(format!("Invalid JSON: {}", err)))?; + .or_else(|err| cx.throw_error(format!("Invalid JSON: {err}")))?; builder .add_assertion(&label, &assertion) .or_else(|err| cx.throw_error(err.to_string()))?; @@ -170,6 +172,28 @@ impl NeonBuilder { Ok(promise) } + + pub fn add_redaction(mut cx: FunctionContext) -> JsResult { + let rt = runtime(); + let this = cx.this::>()?; + let uri = cx.argument::(0)?.value(&mut cx); + let reason = cx.argument::(1)?.value(&mut cx); + let mut builder = rt.block_on(async { this.builder.lock().await }); + builder + .definition + .redactions + .get_or_insert_with(Vec::new) + .push(uri.clone()); + let action = Action::new("c2pa.redacted") + .set_reason(reason) + .set_parameter("redacted".to_owned(), uri) + .or_else(|err| cx.throw_error(err.to_string()))?; + builder + .add_action(action) + .or_else(|err| cx.throw_error(err.to_string()))?; + Ok(cx.undefined()) + } + pub fn add_ingredient(mut cx: FunctionContext) -> JsResult { let rt = runtime(); let this = cx.this::>()?; @@ -254,7 +278,7 @@ impl NeonBuilder { // Block on acquiring the async mutex lock // Settings are automatically applied when runtime() is called let rt = runtime(); - let mut builder = rt.block_on(async { builder.lock().await }); + let builder = rt.block_on(async { builder.lock().await }); dest.write_stream().and_then(|mut dest_stream| { builder.to_archive(&mut dest_stream)?; @@ -302,7 +326,7 @@ impl NeonBuilder { let builder = if let Some(context) = context_opt { Builder::from_context(context).with_archive(source_stream)? } else { - Builder::from_archive(source_stream)? + Builder::default().with_archive(source_stream)? }; Ok(builder) }) diff --git a/src/neon_reader.rs b/src/neon_reader.rs index ce670a5..553c717 100644 --- a/src/neon_reader.rs +++ b/src/neon_reader.rs @@ -70,7 +70,7 @@ impl NeonReader { .with_stream_async(&format, stream) .await? } else { - Reader::from_stream_async(&format, stream).await? + Reader::default().with_stream_async(&format, stream).await? }; Ok(reader) @@ -130,7 +130,9 @@ impl NeonReader { .with_manifest_data_and_stream_async(&c2pa_data, &format, stream) .await? } else { - Reader::from_manifest_data_and_stream_async(&c2pa_data, &format, stream).await? + Reader::default() + .with_manifest_data_and_stream_async(&c2pa_data, &format, stream) + .await? }; Ok(reader) diff --git a/src/neon_trustmark.rs b/src/neon_trustmark.rs index 3d27444..596c97c 100644 --- a/src/neon_trustmark.rs +++ b/src/neon_trustmark.rs @@ -242,9 +242,7 @@ pub fn fetch_model(variant: Variant, dir_path: &std::path::Path) -> Result Result Result> { +fn download_model(rt: &tokio::runtime::Runtime, client: &Client, url: &str) -> Result> { rt.block_on(async { let response = client .get(url) diff --git a/src/utils.rs b/src/utils.rs index 7a2f994..6a53855 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -46,25 +46,27 @@ pub fn parse_settings( Some(js_value) => { if js_value.is_a::(cx) { let settings_string = js_value - .downcast::(cx).map_err(|_| Error::Signing(format!("{}: Expected settings string", error_prefix)))? + .downcast::(cx) + .map_err(|_| { + Error::Signing(format!("{error_prefix}: Expected settings string")) + })? .value(cx); // Create context with settings let context = Context::new() .with_settings(settings_string.as_str()) - .map_err(|e| Error::Signing(format!("{}: Invalid settings: {}", error_prefix, e)))?; - + .map_err(|e| { + Error::Signing(format!("{error_prefix}: Invalid settings: {e}")) + })?; Ok(Some(context)) } else if js_value.is_a::(cx) || js_value.is_a::(cx) { Ok(None) } else { Err(Error::Signing(format!( - "{}: Settings must be a string, null, or undefined", - error_prefix + "{error_prefix}: Settings must be a string, null, or undefined", ))) } } None => Ok(None), } } - diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo index 3ef71dd..c4e010c 100644 --- a/tsconfig.tsbuildinfo +++ b/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./js-src/Builder.spec.ts","./js-src/Builder.ts","./js-src/IdentityAssertion.spec.ts","./js-src/IdentityAssertion.ts","./js-src/Reader.spec.ts","./js-src/Reader.ts","./js-src/Settings.spec.ts","./js-src/Settings.ts","./js-src/Signer.spec.ts","./js-src/Signer.ts","./js-src/Trustmark.spec.ts","./js-src/Trustmark.ts","./js-src/assertions.ts","./js-src/binary.ts","./js-src/index.node.d.ts","./js-src/index.ts","./js-src/types.d.ts"],"version":"5.9.3"} \ No newline at end of file +{"root":["./js-src/builder.spec.ts","./js-src/builder.ts","./js-src/identityassertion.spec.ts","./js-src/identityassertion.ts","./js-src/reader.spec.ts","./js-src/reader.ts","./js-src/settings.spec.ts","./js-src/settings.ts","./js-src/signer.spec.ts","./js-src/signer.ts","./js-src/trustmark.spec.ts","./js-src/trustmark.ts","./js-src/assertions.ts","./js-src/binary.ts","./js-src/index.node.d.ts","./js-src/index.ts","./js-src/types.d.ts"],"version":"5.9.3"} \ No newline at end of file