Skip to main content

genomicframe_core/
io.rs

1//! I/O abstractions for local, async, and cloud storage
2//!
3//! This module provides unified interfaces for reading and writing genomic data
4//! from various sources: local filesystem, cloud storage (S3, GCS, Azure), etc.
5
6use crate::error::{Error, Result};
7use std::path::Path;
8
9/// Supported compression formats
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Compression {
12    /// No compression
13    None,
14    /// Gzip compression (.gz)
15    Gzip,
16    /// Bgzip compression (block gzip, .bgz)
17    Bgzip,
18    /// Zstd compression (.zst)
19    Zstd,
20}
21
22impl Compression {
23    /// Detect compression from file extension
24    pub fn from_path<P: AsRef<Path>>(path: P) -> Self {
25        let path = path.as_ref();
26        if let Some(ext) = path.extension() {
27            match ext.to_str() {
28                Some("gz") => Compression::Gzip,
29                Some("bgz") | Some("bgzf") => Compression::Bgzip,
30                Some("zst") => Compression::Zstd,
31                _ => Compression::None,
32            }
33        } else {
34            Compression::None
35        }
36    }
37}
38
39/// File location (local or remote)
40#[derive(Debug, Clone)]
41pub enum FileLocation {
42    /// Local filesystem path
43    Local(String),
44    /// S3 URI (s3://bucket/key)
45    S3(String),
46    /// GCS URI (gs://bucket/key)
47    Gcs(String),
48    /// Azure Blob Storage URI
49    Azure(String),
50    /// HTTP/HTTPS URL
51    Http(String),
52}
53
54impl FileLocation {
55    /// Parse a file location from a string
56    pub fn parse(s: &str) -> Self {
57        if s.starts_with("s3://") {
58            FileLocation::S3(s.to_string())
59        } else if s.starts_with("gs://") {
60            FileLocation::Gcs(s.to_string())
61        } else if s.starts_with("http://") || s.starts_with("https://") {
62            FileLocation::Http(s.to_string())
63        } else if s.starts_with("az://") || s.starts_with("azure://") {
64            FileLocation::Azure(s.to_string())
65        } else {
66            FileLocation::Local(s.to_string())
67        }
68    }
69
70    /// Check if this is a local file
71    pub fn is_local(&self) -> bool {
72        matches!(self, FileLocation::Local(_))
73    }
74
75    /// Check if this is a remote location
76    pub fn is_remote(&self) -> bool {
77        !self.is_local()
78    }
79}
80
81/// Cloud storage integration (placeholder for Phase 1b)
82pub struct CloudReader;
83
84impl CloudReader {
85    /// Open a cloud file (not yet implemented)
86    pub fn open(_location: FileLocation) -> Result<Self> {
87        Err(Error::NotImplemented(
88            "Cloud storage support will be implemented in Phase 1b".to_string(),
89        ))
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_compression_detection() {
99        assert_eq!(
100            Compression::from_path("file.vcf.gz"),
101            Compression::Gzip
102        );
103        assert_eq!(
104            Compression::from_path("file.vcf.bgz"),
105            Compression::Bgzip
106        );
107        assert_eq!(
108            Compression::from_path("file.vcf"),
109            Compression::None
110        );
111    }
112
113    #[test]
114    fn test_file_location_parsing() {
115        assert!(matches!(
116            FileLocation::parse("/local/path/file.vcf"),
117            FileLocation::Local(_)
118        ));
119        assert!(matches!(
120            FileLocation::parse("s3://bucket/file.vcf"),
121            FileLocation::S3(_)
122        ));
123        assert!(matches!(
124            FileLocation::parse("gs://bucket/file.vcf"),
125            FileLocation::Gcs(_)
126        ));
127    }
128}