Skip to main content

turbopack_core/
update_instruction.rs

1use std::{any::Any, fmt::Debug, sync::Arc};
2
3use serde::Serialize;
4use turbo_tasks::{NonLocalValue, debug::ValueDebugFormat};
5
6trait ErasedUpdateInstruction:
7    erased_serde::Serialize + Debug + Send + Sync + NonLocalValue + 'static
8{
9    fn as_any(&self) -> &dyn Any;
10    fn dyn_eq(&self, other: &dyn Any) -> bool;
11}
12
13impl<T> ErasedUpdateInstruction for T
14where
15    T: Serialize + Eq + Debug + Send + Sync + NonLocalValue + 'static,
16{
17    fn as_any(&self) -> &dyn Any {
18        self
19    }
20
21    fn dyn_eq(&self, other: &dyn Any) -> bool {
22        other.downcast_ref::<Self>() == Some(self)
23    }
24}
25
26erased_serde::serialize_trait_object!(ErasedUpdateInstruction);
27
28#[derive(Clone, Debug, Serialize, ValueDebugFormat, NonLocalValue)]
29#[serde(transparent)]
30pub struct UpdateInstruction(Arc<dyn ErasedUpdateInstruction>);
31
32impl PartialEq for UpdateInstruction {
33    fn eq(&self, other: &Self) -> bool {
34        self.0.dyn_eq(other.0.as_any())
35    }
36}
37
38impl Eq for UpdateInstruction {}
39
40impl UpdateInstruction {
41    pub fn new<T>(instruction: T) -> Self
42    where
43        T: Serialize + Eq + Debug + Send + Sync + NonLocalValue + 'static,
44    {
45        Self(Arc::new(instruction))
46    }
47
48    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
49        self.0.as_any().downcast_ref()
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use serde::Serialize;
56    use turbo_tasks::NonLocalValue;
57
58    use super::UpdateInstruction;
59
60    #[derive(Debug, PartialEq, Eq, Serialize, NonLocalValue)]
61    struct TestInstruction {
62        value: u32,
63    }
64
65    #[test]
66    fn serializes_without_an_extra_wrapper() {
67        let instruction = UpdateInstruction::new(TestInstruction { value: 42 });
68
69        assert_eq!(
70            serde_json::to_value(&instruction).unwrap(),
71            serde_json::json!({ "value": 42 })
72        );
73    }
74
75    #[test]
76    fn downcasts_by_concrete_type() {
77        let instruction = UpdateInstruction::new(TestInstruction { value: 42 });
78
79        assert_eq!(
80            instruction
81                .downcast_ref::<TestInstruction>()
82                .map(|instruction| instruction.value),
83            Some(42)
84        );
85        assert_eq!(
86            instruction,
87            UpdateInstruction::new(TestInstruction { value: 42 })
88        );
89    }
90}