spatialrust_records/
record.rs1use spatialrust_core::{PointCloud, SpatialMetadata};
4
5use crate::{RecordProvenance, RecordsError, RecordsResult, SchemaDescriptor};
6
7#[derive(Clone, Debug, PartialEq)]
9pub struct SpatialRecord {
10 schema: SchemaDescriptor,
11 cloud: PointCloud,
12 provenance: RecordProvenance,
13}
14
15impl SpatialRecord {
16 pub fn try_new(schema: SchemaDescriptor, cloud: PointCloud) -> RecordsResult<Self> {
18 Self::try_new_with_provenance(schema, cloud, RecordProvenance::default())
19 }
20
21 pub fn try_new_with_provenance(
23 schema: SchemaDescriptor,
24 cloud: PointCloud,
25 provenance: RecordProvenance,
26 ) -> RecordsResult<Self> {
27 provenance.validate()?;
28 if cloud.schema() != schema.point_schema() {
29 return Err(RecordsError::SchemaMismatch(
30 "point cloud schema must equal the record schema descriptor".into(),
31 ));
32 }
33 cloud.validate()?;
34 Ok(Self { schema, cloud, provenance })
35 }
36
37 pub fn try_from_cloud(
39 id: impl Into<crate::SchemaId>,
40 version: crate::SchemaVersion,
41 cloud: PointCloud,
42 ) -> RecordsResult<Self> {
43 Self::try_from_cloud_with_provenance(id, version, cloud, RecordProvenance::default())
44 }
45
46 pub fn try_from_cloud_with_provenance(
48 id: impl Into<crate::SchemaId>,
49 version: crate::SchemaVersion,
50 cloud: PointCloud,
51 provenance: RecordProvenance,
52 ) -> RecordsResult<Self> {
53 let schema = SchemaDescriptor::try_new(id, version, cloud.schema().clone())?;
54 Self::try_new_with_provenance(schema, cloud, provenance)
55 }
56
57 #[must_use]
59 pub fn schema(&self) -> &SchemaDescriptor {
60 &self.schema
61 }
62
63 #[must_use]
65 pub fn cloud(&self) -> &PointCloud {
66 &self.cloud
67 }
68
69 #[must_use]
71 pub fn provenance(&self) -> &RecordProvenance {
72 &self.provenance
73 }
74
75 #[must_use]
77 pub fn with_provenance(mut self, provenance: RecordProvenance) -> Self {
78 self.provenance = provenance;
79 self
80 }
81
82 #[must_use]
84 pub fn into_parts(self) -> (SchemaDescriptor, PointCloud, RecordProvenance) {
85 (self.schema, self.cloud, self.provenance)
86 }
87
88 #[must_use]
90 pub fn into_cloud(self) -> PointCloud {
91 self.cloud
92 }
93
94 #[must_use]
96 pub fn metadata(&self) -> &SpatialMetadata {
97 self.cloud.metadata()
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::SpatialRecord;
104 use crate::SchemaVersion;
105 use spatialrust_core::{
106 PointBuffer, PointBufferSet, PointCloud, SpatialMetadata, StandardSchemas,
107 };
108
109 #[test]
110 fn record_rejects_schema_mismatch() {
111 let mut buffers = PointBufferSet::new();
112 buffers.insert("x", PointBuffer::from_f32(vec![0.0]));
113 buffers.insert("y", PointBuffer::from_f32(vec![0.0]));
114 buffers.insert("z", PointBuffer::from_f32(vec![0.0]));
115 let cloud = PointCloud::try_from_parts(
116 StandardSchemas::point_xyz(),
117 buffers,
118 SpatialMetadata::default(),
119 )
120 .unwrap();
121 let rich = SpatialRecord::try_from_cloud("p", SchemaVersion::new(1, 0), {
122 let mut buffers = PointBufferSet::new();
123 buffers.insert("x", PointBuffer::from_f32(vec![0.0]));
124 buffers.insert("y", PointBuffer::from_f32(vec![0.0]));
125 buffers.insert("z", PointBuffer::from_f32(vec![0.0]));
126 buffers.insert("intensity", PointBuffer::from_f32(vec![1.0]));
127 PointCloud::try_from_parts(
128 StandardSchemas::point_xyzi(),
129 buffers,
130 SpatialMetadata::default(),
131 )
132 .unwrap()
133 });
134 assert!(rich.is_ok());
135 let _ = cloud;
136 }
137
138 #[test]
139 fn record_preserves_explicit_provenance() {
140 let mut buffers = PointBufferSet::new();
141 buffers.insert("x", PointBuffer::from_f32(vec![0.0]));
142 buffers.insert("y", PointBuffer::from_f32(vec![0.0]));
143 buffers.insert("z", PointBuffer::from_f32(vec![0.0]));
144 let cloud = PointCloud::try_from_parts(
145 StandardSchemas::point_xyz(),
146 buffers,
147 SpatialMetadata::default(),
148 )
149 .unwrap();
150 let provenance = crate::RecordProvenance::try_new("source-1")
151 .unwrap()
152 .with_stream_id("lidar")
153 .with_sequence(Some(2));
154 let record = SpatialRecord::try_from_cloud_with_provenance(
155 "point",
156 SchemaVersion::new(1, 0),
157 cloud,
158 provenance.clone(),
159 )
160 .unwrap();
161 assert_eq!(record.provenance(), &provenance);
162 assert_eq!(record.with_provenance(provenance.clone()).provenance(), &provenance);
163 }
164}