Skip to main content

oxigdal_3d/
error.rs

1//! Error types for OxiGDAL 3D operations
2
3use std::io;
4use thiserror::Error;
5
6/// Result type for 3D operations
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Error types for 3D visualization and point cloud operations
10#[derive(Error, Debug)]
11pub enum Error {
12    /// I/O error
13    #[error("I/O error: {0}")]
14    Io(#[from] io::Error),
15
16    /// LAS/LAZ format error
17    #[error("LAS/LAZ error: {0}")]
18    Las(String),
19
20    /// LAZ compression error
21    #[error("LAZ compression error: {0}")]
22    LazCompression(String),
23
24    /// Point format error
25    #[error("Unsupported point format: {0}")]
26    UnsupportedPointFormat(u8),
27
28    /// COPC (Cloud Optimized Point Cloud) error
29    #[error("COPC error: {0}")]
30    Copc(String),
31
32    /// EPT (Entwine Point Tiles) error
33    #[error("EPT error: {0}")]
34    Ept(String),
35
36    /// Mesh format error
37    #[error("Mesh format error: {0}")]
38    MeshFormat(String),
39
40    /// OBJ export error
41    #[error("OBJ export error: {0}")]
42    ObjExport(String),
43
44    /// glTF/GLB error
45    #[error("glTF/GLB error: {0}")]
46    Gltf(String),
47
48    /// glTF JSON error
49    #[error("glTF JSON error: {0}")]
50    GltfJson(String),
51
52    /// TIN (Triangulated Irregular Network) error
53    #[error("TIN error: {0}")]
54    Tin(String),
55
56    /// Triangulation error
57    #[error("Triangulation error: {0}")]
58    Triangulation(String),
59
60    /// DEM to mesh conversion error
61    #[error("DEM to mesh conversion error: {0}")]
62    DemToMesh(String),
63
64    /// 3D Tiles error
65    #[error("3D Tiles error: {0}")]
66    Tiles3d(String),
67
68    /// Tileset JSON error
69    #[error("Tileset JSON error: {0}")]
70    TilesetJson(String),
71
72    /// B3DM (Batched 3D Model) error
73    #[error("B3DM error: {0}")]
74    B3dm(String),
75
76    /// PNTS (Point Cloud) tile error
77    #[error("PNTS error: {0}")]
78    Pnts(String),
79
80    /// Classification error
81    #[error("Classification error: {0}")]
82    Classification(String),
83
84    /// Ground classification error
85    #[error("Ground classification error: {0}")]
86    GroundClassification(String),
87
88    /// Spatial indexing error
89    #[error("Spatial index error: {0}")]
90    SpatialIndex(String),
91
92    /// Invalid bounds
93    #[error("Invalid bounds: {0}")]
94    InvalidBounds(String),
95
96    /// Invalid geometry
97    #[error("Invalid geometry: {0}")]
98    InvalidGeometry(String),
99
100    /// Empty dataset
101    #[error("Empty dataset: {0}")]
102    EmptyDataset(String),
103
104    /// Invalid point count
105    #[error("Invalid point count: expected {expected}, got {actual}")]
106    InvalidPointCount {
107        /// Expected number of points
108        expected: usize,
109        /// Actual number of points
110        actual: usize,
111    },
112
113    /// Invalid triangle count
114    #[error("Invalid triangle count: {0}")]
115    InvalidTriangleCount(usize),
116
117    /// Invalid mesh
118    #[error("Invalid mesh: {0}")]
119    InvalidMesh(String),
120
121    /// Missing texture
122    #[error("Missing texture: {0}")]
123    MissingTexture(String),
124
125    /// Invalid texture coordinates
126    #[error("Invalid texture coordinates: {0}")]
127    InvalidTextureCoords(String),
128
129    /// HTTP request error (for COPC, EPT)
130    #[error("HTTP error: {0}")]
131    Http(String),
132
133    /// Range request error
134    #[error("Range request error: {0}")]
135    RangeRequest(String),
136
137    /// JSON parsing error
138    #[error("JSON error: {0}")]
139    Json(String),
140
141    /// UTF-8 encoding error
142    #[error("UTF-8 error: {0}")]
143    Utf8(#[from] std::string::FromUtf8Error),
144
145    /// Base64 decode error
146    #[error("Base64 decode error: {0}")]
147    Base64Decode(String),
148
149    /// Compression error
150    #[error("Compression error: {0}")]
151    Compression(String),
152
153    /// Decompression error
154    #[error("Decompression error: {0}")]
155    Decompression(String),
156
157    /// Memory allocation error
158    #[error("Memory allocation error: {0}")]
159    MemoryAllocation(String),
160
161    /// Octree error
162    #[error("Octree error: {0}")]
163    Octree(String),
164
165    /// Hierarchical LOD error
166    #[error("Hierarchical LOD error: {0}")]
167    HierarchicalLod(String),
168
169    /// Tile loading error
170    #[error("Tile loading error: {0}")]
171    TileLoading(String),
172
173    /// Metadata error
174    #[error("Metadata error: {0}")]
175    Metadata(String),
176
177    /// Invalid header
178    #[error("Invalid header: {0}")]
179    InvalidHeader(String),
180
181    /// Version mismatch
182    #[error("Version mismatch: expected {expected}, got {actual}")]
183    VersionMismatch {
184        /// Expected version
185        expected: String,
186        /// Actual version
187        actual: String,
188    },
189
190    /// Feature not supported
191    #[error("Feature not supported: {0}")]
192    Unsupported(String),
193
194    /// OxiGDAL core error
195    #[error("OxiGDAL core error: {0}")]
196    Core(String),
197}
198
199// Conversion from las crate errors
200impl From<las::Error> for Error {
201    fn from(err: las::Error) -> Self {
202        Error::Las(err.to_string())
203    }
204}
205
206// Conversion from gltf errors
207impl From<gltf::Error> for Error {
208    fn from(err: gltf::Error) -> Self {
209        Error::Gltf(err.to_string())
210    }
211}
212
213// Note: gltf_json::Error is an alias for serde_json::Error
214// so we only need the serde_json conversion
215
216// Conversion from base64 decode errors
217impl From<base64::DecodeError> for Error {
218    fn from(err: base64::DecodeError) -> Self {
219        Error::Base64Decode(err.to_string())
220    }
221}
222
223// Conversion from serde_json errors
224impl From<serde_json::Error> for Error {
225    fn from(err: serde_json::Error) -> Self {
226        Error::Json(err.to_string())
227    }
228}
229
230#[cfg(feature = "async")]
231impl From<reqwest::Error> for Error {
232    fn from(err: reqwest::Error) -> Self {
233        Error::Http(err.to_string())
234    }
235}
236
237impl From<oxigdal_core::error::OxiGdalError> for Error {
238    fn from(err: oxigdal_core::error::OxiGdalError) -> Self {
239        Error::Core(err.to_string())
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[test]
248    fn test_error_display() {
249        let err = Error::Las("test error".to_string());
250        assert_eq!(err.to_string(), "LAS/LAZ error: test error");
251    }
252
253    #[test]
254    fn test_error_from_io() {
255        let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
256        let err: Error = io_err.into();
257        assert!(matches!(err, Error::Io(_)));
258    }
259
260    #[test]
261    fn test_error_from_json() {
262        let json_err = serde_json::from_str::<serde_json::Value>("{invalid}")
263            .expect_err("Should fail to parse invalid JSON");
264        let err: Error = json_err.into();
265        assert!(matches!(err, Error::Json(_)));
266    }
267
268    #[test]
269    fn test_invalid_point_count() {
270        let err = Error::InvalidPointCount {
271            expected: 100,
272            actual: 50,
273        };
274        assert_eq!(err.to_string(), "Invalid point count: expected 100, got 50");
275    }
276
277    #[test]
278    fn test_version_mismatch() {
279        let err = Error::VersionMismatch {
280            expected: "1.4".to_string(),
281            actual: "1.2".to_string(),
282        };
283        assert_eq!(err.to_string(), "Version mismatch: expected 1.4, got 1.2");
284    }
285}