rsdbc_sqlite/options/
parse.rs1use std::borrow::Cow;
5use std::path::{Path, PathBuf};
6use std::str::FromStr;
7use crate::options::SqliteConnectOptions;
8use percent_encoding::percent_decode_str;
9use rsdbc_core::error::RsdbcErrors;
10
11impl FromStr for SqliteConnectOptions {
12 type Err = RsdbcErrors;
13
14 fn from_str(mut uri: &str) -> Result<Self, Self::Err> {
15 let mut options = Self::new();
16
17 uri = uri
19 .trim_start_matches("sqlite://")
20 .trim_start_matches("sqlite:");
21
22 let mut database_and_params = uri.splitn(2, '?');
23
24 let database = database_and_params.next().unwrap_or_default();
25
26 if database == ":memory:" {
27 options.in_memory = true;
28 options.shared_cache = true;
30 options.filename = Cow::Owned(PathBuf::from(database));
31 } else {
32 options.filename = Cow::Owned(
34 Path::new(
35 &*percent_decode_str(database)
36 .decode_utf8()
37 .map_err(|e| RsdbcErrors::config(e.to_string()))?,
38 ).to_path_buf(),
39 );
40 }
41
42 if let Some(params) = database_and_params.next() {
43 for (key, value) in url::form_urlencoded::parse(params.as_bytes()) {
44 match &*key {
45 "mode" => {
50 match &*value {
51 "ro" => {
52 options.read_only = true;
53 }
54
55 "rw" => {}
57
58 "rwc" => {
59 options.create_if_missing = true;
60 }
61
62 "memory" => {
63 options.in_memory = true;
64 options.shared_cache = true;
65 }
66
67 _ => {
68 return Err(RsdbcErrors::Configuration(
69 format!("unknown value {:?} for `mode`", value).into(),
70 ));
71 }
72 }
73 }
74
75 "cache" => match &*value {
79 "private" => {
80 options.shared_cache = false;
81 }
82
83 "shared" => {
84 options.shared_cache = true;
85 }
86
87 _ => {
88 return Err(RsdbcErrors::Configuration(
89 format!("unknown value {:?} for `cache`", value).into(),
90 ));
91 }
92 },
93
94 _ => {
95 return Err(RsdbcErrors::Configuration(
96 format!(
97 "unknown query parameter `{}` while parsing connection URI",
98 key
99 ).into(),
100 ));
101 }
102 }
103 }
104 }
105
106 Ok(options)
107 }
108}
109
110#[test]
111fn parse_in_memory() -> Result<(), RsdbcErrors> {
112 let options: SqliteConnectOptions = "sqlite::memory:".parse()?;
113 assert!(options.in_memory);
114 assert!(options.shared_cache);
115
116 let options: SqliteConnectOptions = "sqlite://?mode=memory".parse()?;
117 assert!(options.in_memory);
118 assert!(options.shared_cache);
119
120 let options: SqliteConnectOptions = "sqlite://:memory:".parse()?;
121 assert!(options.in_memory);
122 assert!(options.shared_cache);
123
124 let options: SqliteConnectOptions = "sqlite://?mode=memory&cache=private".parse()?;
125 assert!(options.in_memory);
126 assert!(!options.shared_cache);
127
128 Ok(())
129}
130
131#[test]
132fn parse_read_only() -> Result<(), RsdbcErrors> {
133 let options: SqliteConnectOptions = "sqlite://a.db?mode=ro".parse()?;
134 assert!(options.read_only);
135 assert_eq!(&*options.filename.to_string_lossy(), "a.db");
136
137 Ok(())
138}
139
140#[test]
141fn parse_shared_in_memory() -> Result<(), RsdbcErrors> {
142 let options: SqliteConnectOptions = "sqlite://a.db?cache=shared".parse()?;
143 assert!(options.shared_cache);
144 assert_eq!(&*options.filename.to_string_lossy(), "a.db");
145
146 Ok(())
147}
148
149#[test]
150fn from_str() -> Result<(), RsdbcErrors> {
151 let options = SqliteConnectOptions::from_str("sqlite://a.db")?;
152 assert_eq!(&*options.filename.to_string_lossy(), "a.db");
153 assert!(!options.shared_cache);
154 assert!(!options.in_memory);
155 assert!(!options.read_only);
156 assert!(!options.create_if_missing);
157
158 Ok(())
159}