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