1use crate::result::{SqliteError, SqliteResult};
2use crate::traits::SqliteRawIo;
3use crate::{error, trace};
4use std::fmt::{Debug, Display};
5use std::fs::File;
6use std::io::Seek;
7use std::io::SeekFrom;
8use std::io::{Cursor, Read};
9use std::path::PathBuf;
10use std::str::FromStr;
11
12pub struct SqliteIo {
16 mode: SqliteIoMode,
17 raw_io: Box<dyn SqliteRawIo>,
18}
19
20impl Debug for SqliteIo {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 f.debug_struct("SqliteIo")
23 .field("mode", &self.mode)
24 .finish()
25 }
26}
27
28#[derive(Debug, PartialEq, Eq)]
29pub enum SqliteIoMode {
30 InMemory,
31 File,
32}
33impl Display for SqliteIoMode {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 write!(f, "{self:?}")
36 }
37}
38impl FromStr for SqliteIoMode {
39 type Err = SqliteError;
40
41 fn from_str(uri_str: &str) -> Result<Self, Self::Err> {
42 let mode = match uri_str.trim() {
43 ":memory:" => SqliteIoMode::InMemory,
44 _ => SqliteIoMode::File,
45 };
46 Ok(mode)
47 }
48}
49impl SqliteIo {
50 pub fn open(input: impl AsRef<str>) -> SqliteResult<Self> {
51 let conn_str = input.as_ref();
52 let mode = conn_str.parse::<SqliteIoMode>()?;
53 match mode {
54 SqliteIoMode::InMemory => {
55 let cursor: Box<Cursor<Vec<u8>>> = Box::new(Cursor::new(vec![]));
56 let raw_io = cursor as Box<dyn SqliteRawIo>;
57 Ok(Self { mode, raw_io })
58 }
59
60 SqliteIoMode::File => {
61 let uri = conn_str.parse::<SqliteUri>()?;
62 let file = Box::new(File::open(uri.path())?);
63 let raw_io: Box<dyn SqliteRawIo> = file as Box<dyn SqliteRawIo>;
64 Ok(Self { mode, raw_io })
65 }
66 }
67 }
68
69 pub fn is_empty(&mut self) -> SqliteResult<bool> {
70 if self.raw_io.read(&mut [0u8; 1])? == 0 {
71 Ok(true)
72 } else {
73 Ok(false)
74 }
75 }
76
77 pub fn read(&mut self, buf: &mut [u8]) -> SqliteResult<usize> {
78 Ok(self.raw_io.read(buf)?)
79 }
80
81 pub fn seek(&mut self, pos: u64) -> SqliteResult<u64> {
82 Ok(self.raw_io.seek(SeekFrom::Start(pos))?)
83 }
84
85 pub fn rewind(&mut self) -> SqliteResult<()> {
86 Ok(self.raw_io.rewind()?)
87 }
88 pub fn stream_position(&mut self) -> SqliteResult<u64> {
89 Ok(self.raw_io.stream_position()?)
90 }
91
92 pub fn close() -> SqliteResult<()> {
93 todo!("Close not yet implemented");
94 }
95
96 pub fn mode(&self) -> &SqliteIoMode {
97 &self.mode
98 }
99}
100
101#[derive(Debug)]
102pub struct SqliteUri {
103 uri: String,
104 path: PathBuf,
105 mode: SqliteUriFileMode,
106}
107
108impl SqliteUri {
109 pub fn path(&self) -> &PathBuf {
110 &self.path
111 }
112}
113impl FromStr for SqliteUri {
114 type Err = SqliteError;
115
116 fn from_str(uri_str: &str) -> Result<Self, Self::Err> {
117 let mut iter_uri = uri_str.split("://");
118 let maybe_schema = iter_uri.next();
119 let maybe_path = iter_uri.next();
120 match (maybe_schema, maybe_path) {
121 (Some(_), Some(path_str)) => {
122 let mut iter_path = path_str.split('?');
123 let file_path = iter_path
124 .next()
125 .ok_or(SqliteError::Custom("Filepath not defined".into()))
126 .map_err(|err| {
127 error!("{err}");
128 err
129 })?;
130 let mode = iter_path
131 .next()
132 .and_then(|s| {
133 trace!("Trying to parse mode [{s}]");
134 s.parse::<SqliteUriFileMode>().ok()
135 })
136 .unwrap_or_default();
137 trace!("{mode:?}");
138 let file_path = PathBuf::from_str(file_path).unwrap();
139 trace!("{file_path:?}");
140 let path = if mode == SqliteUriFileMode::ReadWriteCreate {
141 create_file(&file_path)?;
142 file_path
143 } else {
144 file_path.canonicalize().map_err(|err| {
145 error!("Error on open file [{uri_str}]: [{err}].");
146 error!("Hint: You can change mode to `?mode=rwc` or check you file path.");
147 SqliteError::Custom("Error on parsing file path".into())
148 })?
149 };
150 Ok(Self {
156 uri: uri_str.into(),
157 path,
158 mode,
159 })
160 }
161 _ => {
162 error!("Error on parsing sqlite connection uri[{}]", uri_str);
163 Err(SqliteError::Custom(
164 "Error on parsing sqlite connection uri".into(),
165 ))
166 }
167 }
168 }
169}
170
171#[derive(Debug, Default, PartialEq, Eq)]
178pub enum SqliteUriFileMode {
179 ReadOnly,
180 #[default]
181 ReadWrite,
182 ReadWriteCreate,
183}
184
185impl FromStr for SqliteUriFileMode {
186 type Err = SqliteError;
187
188 fn from_str(s: &str) -> Result<Self, Self::Err> {
189 trace!("impl FromStr for SqliteUriFileMode {s}");
190 match s {
191 "mode=ro" => Ok(Self::ReadOnly),
192 "mode=rw" => Ok(Self::ReadWrite),
193 "mode=rwc" => Ok(Self::ReadWriteCreate),
194 _ => Err(SqliteError::InvalidFileUriMode),
195 }
196 }
197}
198
199fn create_file(path: &PathBuf) -> SqliteResult<()> {
200 let maybe_parent_dir = path.parent();
201 maybe_parent_dir
202 .map(std::fs::create_dir_all)
203 .transpose()?;
204 File::create(path)?;
205 Ok(())
206}