1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::header::SAMPLE_HEADER;
use crate::result::{SqliteError, SqliteResult};
use crate::traits::SqliteRawIo;
use std::fmt::Debug;
use std::fs::File;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::{Cursor, Read};
use std::path::PathBuf;
use std::str::FromStr;

// #[cfg(test)]
// mod tests;

pub struct SqliteIo {
  mode: SqliteIoMode,
  raw_io: Box<dyn SqliteRawIo>,
}

impl Debug for SqliteIo {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    f.debug_struct("SqliteIo")
      .field("mode", &self.mode)
      .finish()
  }
}

// TODO: Sample HEADER (remove)
impl Default for SqliteIo {
  fn default() -> Self {
    let cursor: Box<Cursor<Vec<u8>>> =
      Box::new(Cursor::new(SAMPLE_HEADER.to_vec()));
    let raw_io = cursor as Box<dyn SqliteRawIo>;
    Self {
      mode: SqliteIoMode::InMemory,
      raw_io,
    }
  }
}

#[derive(Debug)]
pub enum SqliteIoMode {
  InMemory,
  File,
}

impl FromStr for SqliteIoMode {
  type Err = SqliteError;

  fn from_str(uri_str: &str) -> Result<Self, Self::Err> {
    let mode = match uri_str.trim() {
      ":memory:" => SqliteIoMode::InMemory,
      _ => SqliteIoMode::File,
    };
    Ok(mode)
  }
}
impl SqliteIo {
  pub fn open(input: impl AsRef<str>) -> SqliteResult<Self> {
    let conn_str = input.as_ref();
    let mode = conn_str.parse::<SqliteIoMode>()?;
    match mode {
      SqliteIoMode::InMemory => {
        let cursor: Box<Cursor<Vec<u8>>> = Box::new(Cursor::new(vec![]));
        let raw_io = cursor as Box<dyn SqliteRawIo>;
        Ok(Self { mode, raw_io })
      }

      SqliteIoMode::File => {
        let uri = conn_str.parse::<SqliteUri>()?;
        let file = Box::new(File::open(uri.path())?);
        let raw_io: Box<dyn SqliteRawIo> = file as Box<dyn SqliteRawIo>;
        Ok(Self { mode, raw_io })
      }
    }
  }

  pub fn read(&mut self, buf: &mut [u8]) -> SqliteResult<usize> {
    Ok(self.raw_io.read(buf)?)
  }

  pub fn seek(&mut self, pos: u64) -> SqliteResult<u64> {
    Ok(self.raw_io.seek(SeekFrom::Start(pos))?)
  }

  pub fn rewind(&mut self) -> SqliteResult<()> {
    Ok(self.raw_io.rewind()?)
  }
  pub fn stream_position(&mut self) -> SqliteResult<u64> {
    Ok(self.raw_io.stream_position()?)
  }

  pub fn close() -> SqliteResult<()> {
    todo!("Close not yet implemented");
  }
}

#[derive(Debug)]
pub struct SqliteUri {
  uri: String,
  path: PathBuf,
}

impl SqliteUri {
  pub fn path(&self) -> &PathBuf {
    &self.path
  }
}
impl FromStr for SqliteUri {
  type Err = SqliteError;

  fn from_str(uri_str: &str) -> Result<Self, Self::Err> {
    // TODO: generate tests for: sqlite:///home/user/db.sqlite3
    let mut iter_uri = uri_str.split("://");
    let maybe_schema = iter_uri.next();
    let maybe_path = iter_uri.next();
    match (maybe_schema, maybe_path) {
      (Some(_), Some(path_str)) => {
        let path = PathBuf::from_str(path_str)
          .unwrap()
          .canonicalize()
          .map_err(|_| {
            SqliteError::Custom("Error on parsing file path".into())
          })?;

        Ok(Self {
          uri: uri_str.into(),
          path,
        })
      }
      _ => Err(SqliteError::Custom(
        "Error on parsing sqlite connection uri".into(),
      )),
    }
  }
}