Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Update region of interest assertion to 2.1 spec #545

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/src/assertions/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ pub mod tests {
assertion::AssertionData,
assertions::{
metadata::{c2pa_source::GENERATOR_REE, DataSource, ReviewRating},
region_of_interest::{Range, RangeType, Time, TimeType},
region_of_interest::{Npt, Range, RangeType, Time},
},
hashed_uri::HashedUri,
};
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/assertions/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ pub mod tests {
#![allow(clippy::unwrap_used)]

use super::*;
use crate::assertions::region_of_interest::{Range, RangeType, Time, TimeType};
use crate::assertions::region_of_interest::{Npt, Range, RangeType, Time};

#[test]
fn assertion_metadata() {
Expand Down
65 changes: 47 additions & 18 deletions sdk/src/assertions/region_of_interest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#[cfg(feature = "json_schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;

use super::Metadata;

Expand Down Expand Up @@ -52,50 +51,69 @@ pub struct Shape {
pub shape_type: ShapeType,
/// The type of unit for the shape range.
pub unit: UnitType,
/// THe origin of the coordinate in the shape.
/// The origin of the coordinate in the shape.
pub origin: Coordinate,
/// The width for rectangles or diameter for circles.
///
/// This field can be ignored for polygons.
#[serde(skip_serializing_if = "Option::is_none")]
pub width: Option<f64>,
/// The height of a rectnagle.
///
/// This field can be ignored for circles and polygons.
#[serde(skip_serializing_if = "Option::is_none")]
pub height: Option<f64>,
/// If the range is inside the shape.
///
/// The default value is true.
#[serde(skip_serializing_if = "Option::is_none")]
pub inside: Option<bool>,
/// The vertices of the polygon.
///
/// This field can be ignored for rectangles and circles.
#[serde(skip_serializing_if = "Option::is_none")]
pub vertices: Option<Vec<Coordinate>>,
}

/// The type of time.
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
/// Normal Play Time (npt) as described in RFC 2326.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase")]
pub enum TimeType {
/// Times are described using Normal Play Time (npt) as described in RFC 2326.
#[default]
Npt,
#[serde(tag = "type", rename = "npt")]
pub struct Npt {
/// The start time or the start of the asset if not present.
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<String>,
/// The end time or the end of the asset if not present.
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<String>,
}

/// A temporal range representing a starting time to an ending time.
#[skip_serializing_none]
/// "Wall Clock Time" using the Internet profile of ISO 8601 as described in RFC 3339.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[skip_serializing_none]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub struct Time {
/// The type of time.
#[serde(rename = "type", default)]
pub time_type: TimeType,
#[serde(tag = "type", rename = "wall-clock")]
pub struct WallClock {
/// The start time or the start of the asset if not present.
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<String>,
/// The end time or the end of the asset if not present.
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<String>,
}

/// A temporal range representing a starting time to an ending time.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
// TODO: workaround for https://github.com/serde-rs/serde/issues/2231
#[serde(untagged)]
pub enum Time {
/// Normal Play Time (npt) as described in RFC 2326.
Npt(Npt),
/// "Wall Clock Time" using the Internet profile of ISO 8601 as described in RFC 3339.
WallClock(WallClock),
}

/// A frame range representing starting and ending frames or pages.
///
/// If both `start` and `end` are missing, the frame will span the entire asset.
Expand All @@ -106,8 +124,10 @@ pub struct Frame {
/// The start of the frame or the end of the asset if not present.
///
/// The first frame/page starts at 0.
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<i32>,
/// The end of the frame inclusive or the end of the asset if not present.
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<i32>,
}

Expand All @@ -123,8 +143,10 @@ pub struct TextSelector {
/// Fragment identifier as per RFC3023 (XML) or ISO 32000-2 (PDF), Annex O.
pub fragment: String,
/// The start character offset or the start of the fragment if not present.
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<i32>,
/// The end character offset or the end of the fragment if not present.
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<i32>,
}

Expand All @@ -136,6 +158,7 @@ pub struct TextSelectorRange {
/// The start (or entire) text range.
pub selector: TextSelector,
/// The end of the text range.
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<TextSelector>,
}

Expand All @@ -147,7 +170,7 @@ pub struct Text {
pub selectors: Vec<TextSelectorRange>,
}

/// Description of the boundaries of an identified range.
/// An identified range representing a specific subset of content in the asset's file container.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub struct Item {
Expand All @@ -171,7 +194,7 @@ pub enum RangeType {
Frame,
/// A textual range, see [`Text`] for more details.
Textual,
/// A range identified by a specific identifier and value, see [`Item`] for more details.
/// An identified range, see [`Item`] for more details.
Identified,
}

Expand All @@ -193,7 +216,7 @@ pub struct Range {
pub frame: Option<Frame>,
/// A textual range.
pub text: Option<Text>,
/// A item identifier.
/// An item identifier.
pub item: Option<Item>,
}

Expand Down Expand Up @@ -242,22 +265,28 @@ pub struct RegionOfInterest {
/// A range describing the region of interest for the specific asset.
pub region: Vec<Range>,
/// A free-text string representing a human-readable name for the region which might be used in a user interface.
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
/// A free-text string representing a machine-readable, unique to this assertion, identifier for the region.
#[serde(skip_serializing_if = "Option::is_none")]
pub identifier: Option<String>,
/// A value from a controlled vocabulary such as <https://cv.iptc.org/newscodes/imageregiontype/> or an entity-specific
/// value (e.g., com.litware.newType) that represents the type of thing(s) depicted by a region.
///
/// Note this field serializes/deserializes into the name `type`.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub region_type: Option<String>,
/// A value from our controlled vocabulary or an entity-specific value (e.g., com.litware.coolArea) that represents
/// the role of a region among other regions.
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<Role>,
/// A free-text string.
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
// If we didn't have a box, `Metadata` would recursively use `RegionOfInterest` causing an infinite size error.
//
/// Additional information about the asset.
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Box<Metadata>>,
}