cdbc_sqlite/options/
parse.rs1use cdbc::error::Error;
2use crate::SqliteConnectOptions;
3use percent_encoding::percent_decode_str;
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:sqlx-in-memory-{}", seqno)));
33 } else {
34 options.filename = Cow::Owned(
36 Path::new(
37 &*percent_decode_str(database)
38 .decode_utf8()
39 .map_err(Error::config)?,
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::Configuration(
72 format!("unknown value {:?} for `mode`", value).into(),
73 ));
74 }
75 }
76 }
77
78 "cache" => match &*value {
82 "private" => {
83 options.shared_cache = false;
84 }
85
86 "shared" => {
87 options.shared_cache = true;
88 }
89
90 _ => {
91 return Err(Error::Configuration(
92 format!("unknown value {:?} for `cache`", value).into(),
93 ));
94 }
95 },
96
97 "immutable" => match &*value {
98 "true" | "1" => {
99 options.immutable = true;
100 }
101 "false" | "0" => {
102 options.immutable = false;
103 }
104 _ => {
105 return Err(Error::Configuration(
106 format!("unknown value {:?} for `immutable`", value).into(),
107 ));
108 }
109 },
110
111 _ => {
112 return Err(Error::Configuration(
113 format!(
114 "unknown query parameter `{}` while parsing connection URI",
115 key
116 )
117 .into(),
118 ));
119 }
120 }
121 }
122 }
123
124 Ok(options)
125 }
126}
127
128#[test]
129fn test_parse_in_memory() -> Result<(), Error> {
130 let options: SqliteConnectOptions = "sqlite::memory:".parse()?;
131 assert!(options.in_memory);
132 assert!(options.shared_cache);
133
134 let options: SqliteConnectOptions = "sqlite://?mode=memory".parse()?;
135 assert!(options.in_memory);
136 assert!(options.shared_cache);
137
138 let options: SqliteConnectOptions = "sqlite://:memory:".parse()?;
139 assert!(options.in_memory);
140 assert!(options.shared_cache);
141
142 let options: SqliteConnectOptions = "sqlite://?mode=memory&cache=private".parse()?;
143 assert!(options.in_memory);
144 assert!(!options.shared_cache);
145
146 Ok(())
147}
148
149#[test]
150fn test_parse_read_only() -> Result<(), Error> {
151 let options: SqliteConnectOptions = "sqlite://a.db?mode=ro".parse()?;
152 assert!(options.read_only);
153 assert_eq!(&*options.filename.to_string_lossy(), "a.db");
154
155 Ok(())
156}
157
158#[test]
159fn test_parse_shared_in_memory() -> Result<(), Error> {
160 let options: SqliteConnectOptions = "sqlite://a.db?cache=shared".parse()?;
161 assert!(options.shared_cache);
162 assert_eq!(&*options.filename.to_string_lossy(), "a.db");
163
164 Ok(())
165}