Skip to main content

oxigdal_core/io/
mod.rs

1//! I/O abstractions for geospatial data access
2//!
3//! This module provides traits and implementations for reading and writing
4//! geospatial data from various sources.
5//!
6//! # Features
7//!
8//! - [`DataSource`] - Synchronous data source trait
9//! - `AsyncDataSource` - Asynchronous data source trait (requires `async` feature)
10//! - [`ByteRange`] - Byte range specification for partial reads
11//! - [`RasterRead`] / [`RasterWrite`] - Raster-specific I/O traits
12
13mod traits;
14
15pub use traits::{
16    ByteRange, CogSupport, DataSink, DataSource, OverviewSupport, RasterRead, RasterWrite,
17};
18
19#[cfg(feature = "async")]
20pub use traits::{AsyncDataSource, AsyncRasterRead};
21
22#[cfg(feature = "std")]
23mod file {
24    //! File-based data source implementation
25
26    use std::fs::File;
27    use std::io::{Read, Seek, SeekFrom};
28    use std::path::{Path, PathBuf};
29    use std::sync::Mutex;
30
31    use crate::error::{IoError, OxiGdalError, Result};
32    use crate::io::{ByteRange, DataSource};
33
34    /// A file-based data source
35    pub struct FileDataSource {
36        path: PathBuf,
37        file: Mutex<File>,
38        size: u64,
39    }
40
41    impl FileDataSource {
42        /// Opens a file as a data source
43        ///
44        /// # Errors
45        /// Returns an error if the file cannot be opened
46        pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
47            let path = path.as_ref().to_path_buf();
48            let file = File::open(&path).map_err(|e| {
49                OxiGdalError::Io(IoError::Read {
50                    message: format!("Failed to open file '{}': {}", path.display(), e),
51                })
52            })?;
53
54            let metadata = file.metadata().map_err(|e| {
55                OxiGdalError::Io(IoError::Read {
56                    message: format!("Failed to get file metadata: {e}"),
57                })
58            })?;
59
60            Ok(Self {
61                path,
62                file: Mutex::new(file),
63                size: metadata.len(),
64            })
65        }
66
67        /// Returns the file path
68        #[must_use]
69        pub fn path(&self) -> &Path {
70            &self.path
71        }
72    }
73
74    impl DataSource for FileDataSource {
75        fn size(&self) -> Result<u64> {
76            Ok(self.size)
77        }
78
79        fn read_range(&self, range: ByteRange) -> Result<Vec<u8>> {
80            let mut file = self.file.lock().map_err(|e| OxiGdalError::Internal {
81                message: format!("Failed to lock file mutex: {e}"),
82            })?;
83
84            file.seek(SeekFrom::Start(range.start)).map_err(|_| {
85                OxiGdalError::Io(IoError::Seek {
86                    position: range.start,
87                })
88            })?;
89
90            let len = range.len() as usize;
91            let mut buffer = vec![0u8; len];
92            file.read_exact(&mut buffer).map_err(|e| {
93                OxiGdalError::Io(IoError::Read {
94                    message: format!(
95                        "Failed to read {} bytes at offset {}: {}",
96                        len, range.start, e
97                    ),
98                })
99            })?;
100
101            Ok(buffer)
102        }
103    }
104
105    impl std::fmt::Debug for FileDataSource {
106        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107            f.debug_struct("FileDataSource")
108                .field("path", &self.path)
109                .field("size", &self.size)
110                .finish()
111        }
112    }
113}
114
115#[cfg(feature = "std")]
116pub use file::FileDataSource;