oharness_core/
trajectory.rs1use crate::event::Event;
7use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9use std::sync::Arc;
10
11#[derive(Clone)]
15pub struct TrajectoryHandle {
16 source: TrajectorySource,
17}
18
19#[derive(Clone)]
20pub enum TrajectorySource {
21 File(PathBuf),
22 InMemory(Arc<Vec<Event>>),
23}
24
25impl std::fmt::Debug for TrajectoryHandle {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match &self.source {
28 TrajectorySource::File(p) => write!(f, "TrajectoryHandle::File({})", p.display()),
29 TrajectorySource::InMemory(v) => {
30 write!(f, "TrajectoryHandle::InMemory(len={})", v.len())
31 }
32 }
33 }
34}
35
36impl TrajectoryHandle {
37 pub fn from_path(path: impl Into<PathBuf>) -> Self {
38 Self {
39 source: TrajectorySource::File(path.into()),
40 }
41 }
42
43 pub fn in_memory(events: Vec<Event>) -> Self {
44 Self {
45 source: TrajectorySource::InMemory(Arc::new(events)),
46 }
47 }
48
49 pub fn source(&self) -> &TrajectorySource {
50 &self.source
51 }
52
53 pub fn path(&self) -> Option<&std::path::Path> {
55 match &self.source {
56 TrajectorySource::File(p) => Some(p),
57 TrajectorySource::InMemory(_) => None,
58 }
59 }
60
61 pub fn summarize(&self) -> TrajectorySummary {
62 match &self.source {
63 TrajectorySource::File(p) => TrajectorySummary {
64 event_count: None,
65 path: Some(p.clone()),
66 in_memory: false,
67 },
68 TrajectorySource::InMemory(v) => TrajectorySummary {
69 event_count: Some(v.len()),
70 path: None,
71 in_memory: true,
72 },
73 }
74 }
75
76 pub fn in_memory_events(&self) -> Option<&Arc<Vec<Event>>> {
80 match &self.source {
81 TrajectorySource::InMemory(v) => Some(v),
82 TrajectorySource::File(_) => None,
83 }
84 }
85}
86
87impl Serialize for TrajectoryHandle {
88 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
89 use serde::ser::SerializeMap;
90 match &self.source {
91 TrajectorySource::File(p) => {
92 let mut m = s.serialize_map(Some(2))?;
93 m.serialize_entry("type", "file")?;
94 m.serialize_entry("path", p)?;
95 m.end()
96 }
97 TrajectorySource::InMemory(_) => Err(serde::ser::Error::custom(
98 "in-memory TrajectoryHandle cannot be serialized; materialize to a file first",
99 )),
100 }
101 }
102}
103
104impl<'de> Deserialize<'de> for TrajectoryHandle {
105 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
106 #[derive(Deserialize)]
107 struct Wire {
108 #[serde(rename = "type")]
109 kind: String,
110 path: Option<PathBuf>,
111 }
112 let w = Wire::deserialize(d)?;
113 match w.kind.as_str() {
114 "file" => {
115 let p = w
116 .path
117 .ok_or_else(|| serde::de::Error::custom("file trajectory missing `path`"))?;
118 Ok(TrajectoryHandle::from_path(p))
119 }
120 other => Err(serde::de::Error::custom(format!(
121 "unknown trajectory source `{other}`"
122 ))),
123 }
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct TrajectorySummary {
129 pub event_count: Option<usize>,
130 pub path: Option<PathBuf>,
131 pub in_memory: bool,
132}
133
134#[derive(Debug, thiserror::Error)]
135pub enum TrajectoryError {
136 #[error("trajectory I/O: {0}")]
137 Io(#[from] std::io::Error),
138 #[error("trajectory JSON decode: {0}")]
139 Decode(#[from] serde_json::Error),
140 #[error("payload sidecar missing: {0}")]
141 PayloadNotFound(String),
142 #[error("unsupported trajectory source for this operation")]
143 Unsupported,
144}