Skip to main content

motion_core/asset/
environment.rs

1//! Immutable, type-erased content environments carried by core scene contracts.
2
3use std::{
4    any::Any,
5    fmt,
6    sync::{Arc, Mutex},
7};
8
9/// Explicit font-source observations shared by rendering workers for one export.
10#[derive(Clone, Default, Debug)]
11pub struct FontUsage(Arc<Mutex<Vec<FontIdentity>>>);
12
13impl FontUsage {
14    /// Records a selected face once, including when its outline came from a cache.
15    pub fn record(&self, font: FontIdentity) {
16        let mut fonts = self.0.lock().unwrap_or_else(|error| error.into_inner());
17        if !fonts.contains(&font) {
18            fonts.push(font);
19        }
20    }
21
22    /// Returns stable face ordering independent of worker completion order.
23    pub fn fonts(&self) -> Vec<FontIdentity> {
24        let mut fonts = self
25            .0
26            .lock()
27            .unwrap_or_else(|error| error.into_inner())
28            .clone();
29        fonts.sort_by(|left, right| {
30            (&left.name, left.file_hash).cmp(&(&right.name, right.file_hash))
31        });
32        fonts
33    }
34}
35
36/// A frozen content environment with an explicit cache identity.
37/// The content package owns its concrete font implementation; core only carries
38/// its immutable snapshot and never initializes a font engine.
39#[derive(Clone)]
40pub struct FontEnvironment {
41    value: Arc<dyn Any + Send + Sync>,
42    identity: u64,
43    usage: Option<Arc<dyn Fn() -> Vec<FontIdentity> + Send + Sync>>,
44}
45
46impl std::panic::RefUnwindSafe for FontEnvironment {}
47
48impl std::panic::UnwindSafe for FontEnvironment {}
49
50impl From<&FontEnvironment> for FontEnvironment {
51    fn from(value: &FontEnvironment) -> Self {
52        value.clone()
53    }
54}
55
56impl FontEnvironment {
57    /// Captures an immutable environment under its owning implementation's identity.
58    pub fn new<T: Any + Send + Sync>(value: T, identity: u64) -> Self {
59        Self {
60            value: Arc::new(value),
61            identity,
62            usage: None,
63        }
64    }
65
66    /// Stable identity of this immutable environment snapshot.
67    pub fn snapshot_id(&self) -> u64 {
68        self.identity
69    }
70
71    /// Attaches explicit usage reporting without exposing the content engine to core.
72    pub fn with_usage(
73        mut self,
74        usage: impl Fn() -> Vec<FontIdentity> + Send + Sync + 'static,
75    ) -> Self {
76        self.usage = Some(Arc::new(usage));
77        self
78    }
79
80    /// Faces actually selected from this shared environment, including source hashes.
81    pub fn used_fonts(&self) -> Vec<FontIdentity> {
82        self.usage.as_ref().map_or_else(Vec::new, |usage| usage())
83    }
84
85    /// Resolves an environment only in the package that owns its concrete type.
86    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
87        self.value.downcast_ref()
88    }
89}
90
91impl fmt::Debug for FontEnvironment {
92    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
93        formatter
94            .debug_struct("FontEnvironment")
95            .field("snapshot", &self.identity)
96            .finish_non_exhaustive()
97    }
98}
99
100/// Identity of one parsed font face, in primary-then-fallback order.
101/// Hashes identify the font source, not a cryptographic checksum or a promise of
102/// identical rasterization across different text-engine versions/platforms.
103#[derive(Clone, Debug, PartialEq, Eq)]
104pub struct FontIdentity {
105    /// Face name when present in the font's metadata.
106    pub name: Option<String>,
107    /// Non-cryptographic font-source hash supplied by the text engine.
108    pub file_hash: u64,
109}