1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::error::Error;

use crate::loader::LoaderEvent;
use crate::ArtifactTypeId;
use crossbeam_channel::Sender;
use hydrate_base::handle::LoaderInfoProvider;
use hydrate_base::{ArtifactId, LoadHandle, StringHash};

#[derive(Debug)]
pub enum HandleOp {
    Error(LoadHandle, Box<dyn Error + Send>),
    Complete(LoadHandle),
    Drop(LoadHandle),
}

/// Type that allows the downstream artifact storage implementation to signal that an artifact update
/// operation has completed. See [`ArtifactStorage::load_artifact`].
pub struct ArtifactLoadOp {
    sender: Option<Sender<LoaderEvent>>,
    handle: LoadHandle,
}

impl ArtifactLoadOp {
    pub(crate) fn new(
        sender: Sender<LoaderEvent>,
        handle: LoadHandle,
    ) -> Self {
        Self {
            sender: Some(sender),
            handle,
        }
    }

    /// Returns the `LoadHandle` associated with the load operation
    pub fn load_handle(&self) -> LoadHandle {
        self.handle
    }

    /// Signals that this load operation has completed succesfully.
    pub fn complete(mut self) {
        log::debug!("LoadOp for {:?} complete", self.handle);
        let _ = self
            .sender
            .as_ref()
            .unwrap()
            .send(LoaderEvent::LoadResult(HandleOp::Complete(self.handle)));
        self.sender = None;
    }

    /// Signals that this load operation has completed with an error.
    pub fn error<E: Error + 'static + Send>(
        mut self,
        error: E,
    ) {
        println!("LoadOp for {:?} error {:?}", self.handle, error);
        let _ = self
            .sender
            .as_ref()
            .unwrap()
            .send(LoaderEvent::LoadResult(HandleOp::Error(
                self.handle,
                Box::new(error),
            )));
        self.sender = None;
    }
}

impl Drop for ArtifactLoadOp {
    fn drop(&mut self) {
        if let Some(ref sender) = self.sender {
            let _ = sender.send(LoaderEvent::LoadResult(HandleOp::Drop(self.handle)));
        }
    }
}

/// Storage for all artifacts of all artifact types.
///
/// Consumers are expected to provide the implementation for this, as this is the bridge between
/// [`Loader`](crate::loader::Loader) and the application.
pub trait ArtifactStorage {
    /// Updates the backing data of an artifact.
    ///
    /// An example usage of this is when a texture such as "player.png" changes while the
    /// application is running. The artifact ID is the same, but the underlying pixel data can differ.
    ///
    /// # Parameters
    ///
    /// * `loader`: Loader implementation calling this function.
    /// * `artifact_type_id`: UUID of the artifact type.
    /// * `data`: The updated artifact byte data.
    /// * `load_handle`: ID allocated by [`Loader`](crate::loader::Loader) to track loading of a particular artifact.
    /// * `load_op`: Allows the loading implementation to signal when loading is done / errors.
    fn load_artifact(
        &mut self,
        loader_info: &dyn LoaderInfoProvider,
        artifact_type_id: &ArtifactTypeId,
        artifact_id: ArtifactId,
        data: Vec<u8>,
        load_handle: LoadHandle,
        load_op: ArtifactLoadOp,
    ) -> Result<(), Box<dyn Error + Send + 'static>>;

    /// Commits the specified artifact as loaded and ready to use.
    ///
    /// # Parameters
    ///
    /// * `artifact_type`: UUID of the artifact type.
    /// * `load_handle`: ID allocated by [`Loader`](crate::loader::Loader) to track loading of a particular artifact.
    fn commit_artifact(
        &mut self,
        artifact_type: ArtifactTypeId,
        load_handle: LoadHandle,
    );

    /// Frees the artifact identified by the load handle.
    ///
    /// # Parameters
    ///
    /// * `artifact_type_id`: UUID of the artifact type.
    /// * `load_handle`: ID allocated by [`Loader`](crate::loader::Loader) to track loading of a particular artifact.
    fn free_artifact(
        &mut self,
        artifact_type_id: ArtifactTypeId,
        load_handle: LoadHandle,
    );
}

/// An indirect identifier that can be resolved to a specific [`ArtifactID`] by an [`IndirectionResolver`] impl.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum IndirectIdentifier {
    ArtifactId(ArtifactId, ArtifactTypeId),
    SymbolWithType(StringHash, ArtifactTypeId),
}