Skip to main content

rawshift_core/
codec.rs

1//! Codec identity and shared encode vocabulary.
2//!
3//! These types are format-agnostic so `rawshift-image` and a future
4//! `rawshift-video` can describe their codecs with one shared vocabulary.
5
6use std::fmt;
7
8/// A stable identifier for one codec implementation, in `"{format}/{impl}"` form.
9///
10/// For example `"jpeg/mozjpeg"` or `"avif/ravif"`. The string is stable across
11/// releases, so callers may use it as part of a cache key.
12///
13/// Only [`Serialize`](serde::Serialize) is derived: the `&'static str` is
14/// discovered at runtime by enumerating compiled codecs, never deserialized.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize))]
17pub struct CodecId {
18    /// The `"{format}/{impl}"` identifier string.
19    pub id: &'static str,
20}
21
22impl CodecId {
23    /// Create a codec id from a static `"{format}/{impl}"` string.
24    pub const fn new(id: &'static str) -> Self {
25        Self { id }
26    }
27}
28
29impl fmt::Display for CodecId {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.write_str(self.id)
32    }
33}
34
35/// Whether a codec encodes or decodes.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38pub enum CodecDirection {
39    /// Produces an encoded image from pixels.
40    Encode,
41    /// Produces pixels from an encoded image.
42    Decode,
43}
44
45/// Identity and version of one compiled-in codec implementation.
46///
47/// Returned by `available_encoders` / `available_decoders` in `rawshift-image`.
48/// Serialize-only, because it embeds a [`CodecId`].
49#[derive(Debug, Clone, PartialEq, Eq, Hash)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize))]
51pub struct CodecInfo {
52    /// Stable implementation identifier.
53    pub id: CodecId,
54    /// Backend version string — a crate version for pure-Rust backends, or a
55    /// runtime-reported library version for C/C++ backends.
56    pub version: String,
57    /// Whether this entry encodes or decodes.
58    pub direction: CodecDirection,
59}
60
61impl CodecInfo {
62    /// Construct a [`CodecInfo`].
63    pub fn new(id: CodecId, version: impl Into<String>, direction: CodecDirection) -> Self {
64        Self {
65            id,
66            version: version.into(),
67            direction,
68        }
69    }
70}
71
72/// Controls which metadata blocks an encoder embeds into its output container.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75pub struct MetadataEmbedOptions {
76    /// Embed EXIF metadata. Default: `true`.
77    pub embed_exif: bool,
78    /// Embed the ICC color profile. Default: `true`.
79    pub embed_icc: bool,
80    /// Embed XMP metadata. Default: `true`.
81    pub embed_xmp: bool,
82}
83
84impl MetadataEmbedOptions {
85    /// Embed every supported metadata block (this is also [`Default`]).
86    pub fn all() -> Self {
87        Self {
88            embed_exif: true,
89            embed_icc: true,
90            embed_xmp: true,
91        }
92    }
93
94    /// Embed no metadata.
95    pub fn none() -> Self {
96        Self {
97            embed_exif: false,
98            embed_icc: false,
99            embed_xmp: false,
100        }
101    }
102
103    /// True if at least one metadata block is requested.
104    pub fn any(&self) -> bool {
105        self.embed_exif || self.embed_icc || self.embed_xmp
106    }
107}
108
109impl Default for MetadataEmbedOptions {
110    fn default() -> Self {
111        Self::all()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn codec_id_display() {
121        let id = CodecId::new("jpeg/mozjpeg");
122        assert_eq!(id.to_string(), "jpeg/mozjpeg");
123    }
124
125    #[test]
126    fn metadata_embed_constructors() {
127        assert_eq!(MetadataEmbedOptions::default(), MetadataEmbedOptions::all());
128        assert!(MetadataEmbedOptions::all().any());
129        assert!(!MetadataEmbedOptions::none().any());
130    }
131}