modewise_pipeline/
artifact.rs1use std::fmt;
2
3use modewise_brain::ChannelTable;
4use modewise_connectivity::FunctionalConnectivity;
5use modewise_decomposition::{Decomposition, InstantaneousModes, Loadings};
6use modewise_epochs::{Epochs, Events};
7use modewise_signals::MultichannelSeries;
8use modewise_spectral::TimeFrequencyMap;
9use serde::{Deserialize, Serialize};
10use sha2::{Digest, Sha256};
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum Artifact {
15 Series(MultichannelSeries),
16 Decomposition(Decomposition),
17 TimeFrequency(TimeFrequencyMap),
18 Events(Events),
19 Epochs(Epochs),
20 Instantaneous(InstantaneousModes),
21 Loadings(Loadings),
22 FunctionalConnectivity(FunctionalConnectivity),
23 Channels(ChannelTable),
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub enum ArtifactKind {
29 Series,
30 Decomposition,
31 TimeFrequency,
32 Events,
33 Epochs,
34 Instantaneous,
35 Loadings,
36 FunctionalConnectivity,
37 Channels,
38}
39
40impl Artifact {
41 pub fn kind(&self) -> ArtifactKind {
42 match self {
43 Self::Series(_) => ArtifactKind::Series,
44 Self::Decomposition(_) => ArtifactKind::Decomposition,
45 Self::TimeFrequency(_) => ArtifactKind::TimeFrequency,
46 Self::Events(_) => ArtifactKind::Events,
47 Self::Epochs(_) => ArtifactKind::Epochs,
48 Self::Instantaneous(_) => ArtifactKind::Instantaneous,
49 Self::Loadings(_) => ArtifactKind::Loadings,
50 Self::FunctionalConnectivity(_) => ArtifactKind::FunctionalConnectivity,
51 Self::Channels(_) => ArtifactKind::Channels,
52 }
53 }
54}
55
56impl fmt::Display for ArtifactKind {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 let name = match self {
59 Self::Series => "series",
60 Self::Decomposition => "decomposition",
61 Self::TimeFrequency => "time_frequency",
62 Self::Events => "events",
63 Self::Epochs => "epochs",
64 Self::Instantaneous => "instantaneous",
65 Self::Loadings => "loadings",
66 Self::FunctionalConnectivity => "functional_connectivity",
67 Self::Channels => "channels",
68 };
69 f.write_str(name)
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
75#[serde(transparent)]
76pub struct ArtifactKey(String);
77
78impl ArtifactKey {
79 pub(crate) fn from_bytes(bytes: &[u8]) -> Self {
80 let digest = Sha256::digest(bytes);
81 Self(digest.iter().map(|b| format!("{b:02x}")).collect())
82 }
83
84 pub(crate) fn of_series(series: &MultichannelSeries) -> Self {
86 let mut bytes = b"series".to_vec();
87 bytes.extend(series.sampling_rate().hz().to_le_bytes());
88 bytes.extend(series.n_channels().to_le_bytes());
89 bytes.extend(series.n_samples().to_le_bytes());
90 for label in series.labels() {
91 bytes.extend(label.len().to_le_bytes());
92 bytes.extend(label.as_bytes());
93 }
94 for value in series.data().iter() {
95 bytes.extend(value.to_le_bytes());
96 }
97 Self::from_bytes(&bytes)
98 }
99
100 pub(crate) fn of_events(events: &Events) -> Self {
101 let mut bytes = b"events".to_vec();
102 for e in events.as_slice() {
103 bytes.extend(e.onset_s.to_le_bytes());
104 bytes.extend(e.duration_s.to_le_bytes());
105 bytes.extend(e.label.len().to_le_bytes());
106 bytes.extend(e.label.as_bytes());
107 }
108 Self::from_bytes(&bytes)
109 }
110
111 pub(crate) fn of_channels(table: &ChannelTable) -> Self {
112 let mut bytes = b"channels".to_vec();
113 let mut text = |s: &str| {
114 bytes.extend(s.len().to_le_bytes());
115 bytes.extend(s.as_bytes());
116 };
117 for c in table.channels() {
118 text(c.name());
119 text(c.region().map_or("\0", |r| r.as_str()));
120 text(c.hemisphere().map_or("\0", |h| h.as_str()));
121 for (k, v) in c.tags() {
122 text(k);
123 text(v);
124 }
125 text("\0end");
126 }
127 Self::from_bytes(&bytes)
128 }
129
130 pub fn as_str(&self) -> &str {
131 &self.0
132 }
133}
134
135impl fmt::Display for ArtifactKey {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.write_str(&self.0)
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn keys_are_lowercase_sha256_hex() {
147 let key = ArtifactKey::from_bytes(b"abc");
148 assert_eq!(
149 key.as_str(),
150 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
151 );
152 assert_eq!(key.to_string(), key.as_str());
153 }
154}