/
germanubis
/
sqlx
Обзор
Документация
Войти
/
germanubis
/
sqlx
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
sqlx-sqlite/src/options/parse.rs
231 строка
8 KB
Austin Bonander
fix(sqlite): fix unit and doctests
24 авг 2024, 09:39
24 авг 2024, 09:39
394a7e8
Код
Авторство
О чём код?
use std::borrow::Cow; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::atomic::{AtomicUsize, Ordering}; use percent_encoding::{percent_decode_str, percent_encode, AsciiSet}; use url::Url; use crate::error::Error; use crate::SqliteConnectOptions; // https://www.sqlite.org/uri.html static IN_MEMORY_DB_SEQ: AtomicUsize = AtomicUsize::new(0); impl SqliteConnectOptions { pub(crate) fn from_db_and_params(database: &str, params: Option<&str>) -> Result<Self, Error> { let mut options = Self::default(); if database == ":memory:" { options.in_memory = true; options.shared_cache = true; let seqno = IN_MEMORY_DB_SEQ.fetch_add(1, Ordering::Relaxed); options.filename = Cow::Owned(PathBuf::from(format!("file:sqlx-in-memory-{seqno}"))); } else { // % decode to allow for `?` or `#` in the filename options.filename = Cow::Owned( Path::new( &*percent_decode_str(database) .decode_utf8() .map_err(Error::config)?, ) .to_path_buf(), ); } if let Some(params) = params { for (key, value) in url::form_urlencoded::parse(params.as_bytes()) { match &*key { // The mode query parameter determines if the new database is opened read-only, // read-write, read-write and created if it does not exist, or that the // database is a pure in-memory database that never interacts with disk, // respectively. "mode" => { match &*value { "ro" => { options.read_only = true; } // default "rw" => {} "rwc" => { options.create_if_missing = true; } "memory" => { options.in_memory = true; options.shared_cache = true; } _ => { return Err(Error::Configuration( format!("unknown value {value:?} for `mode`").into(), )); } } } // The cache query parameter specifies the cache behaviour across multiple // connections to the same database within the process. A shared cache is // essential for persisting data across connections to an in-memory database. "cache" => match &*value { "private" => { options.shared_cache = false; } "shared" => { options.shared_cache = true; } _ => { return Err(Error::Configuration( format!("unknown value {value:?} for `cache`").into(), )); } }, "immutable" => match &*value { "true" | "1" => { options.immutable = true; } "false" | "0" => { options.immutable = false; } _ => { return Err(Error::Configuration( format!("unknown value {value:?} for `immutable`").into(), )); } }, "vfs" => options.vfs = Some(Cow::Owned(value.into_owned())), _ => { return Err(Error::Configuration( format!("unknown query parameter `{key}` while parsing connection URL") .into(), )); } } } } Ok(options) } pub(crate) fn build_url(&self) -> Url { // https://url.spec.whatwg.org/#path-percent-encode-set static PATH_ENCODE_SET: AsciiSet = percent_encoding::CONTROLS .add(b' ') .add(b'"') .add(b'#') .add(b'<') .add(b'>') .add(b'?') .add(b'`') .add(b'{') .add(b'}'); let filename_encoded = percent_encode( self.filename.as_os_str().as_encoded_bytes(), &PATH_ENCODE_SET, ); let mut url = Url::parse(&format!("sqlite://{filename_encoded}")) .expect("BUG: generated un-parseable URL"); let mode = match (self.in_memory, self.create_if_missing, self.read_only) { (true, _, _) => "memory", (false, true, _) => "rwc", (false, false, true) => "ro", (false, false, false) => "rw", }; url.query_pairs_mut().append_pair("mode", mode); let cache = match self.shared_cache { true => "shared", false => "private", }; url.query_pairs_mut().append_pair("cache", cache); if self.immutable { url.query_pairs_mut().append_pair("immutable", "true"); } if let Some(vfs) = &self.vfs { url.query_pairs_mut().append_pair("vfs", vfs); } url } } impl FromStr for SqliteConnectOptions { type Err = Error; fn from_str(mut url: &str) -> Result<Self, Self::Err> { // remove scheme from the URL url = url .trim_start_matches("sqlite://") .trim_start_matches("sqlite:"); let mut database_and_params = url.splitn(2, '?'); let database = database_and_params.next().unwrap_or_default(); let params = database_and_params.next(); Self::from_db_and_params(database, params) } } #[test] fn test_parse_in_memory() -> Result<(), Error> { let options: SqliteConnectOptions = "sqlite::memory:".parse()?; assert!(options.in_memory); assert!(options.shared_cache); let options: SqliteConnectOptions = "sqlite://?mode=memory".parse()?; assert!(options.in_memory); assert!(options.shared_cache); let options: SqliteConnectOptions = "sqlite://:memory:".parse()?; assert!(options.in_memory); assert!(options.shared_cache); let options: SqliteConnectOptions = "sqlite://?mode=memory&cache=private".parse()?; assert!(options.in_memory); assert!(!options.shared_cache); Ok(()) } #[test] fn test_parse_read_only() -> Result<(), Error> { let options: SqliteConnectOptions = "sqlite://a.db?mode=ro".parse()?; assert!(options.read_only); assert_eq!(&*options.filename.to_string_lossy(), "a.db"); Ok(()) } #[test] fn test_parse_shared_in_memory() -> Result<(), Error> { let options: SqliteConnectOptions = "sqlite://a.db?cache=shared".parse()?; assert!(options.shared_cache); assert_eq!(&*options.filename.to_string_lossy(), "a.db"); Ok(()) } #[test] fn it_returns_the_parsed_url() -> Result<(), Error> { let url = "sqlite://test.db?mode=rw&cache=shared"; let options: SqliteConnectOptions = url.parse()?; let expected_url = Url::parse(url).unwrap(); assert_eq!(options.build_url(), expected_url); Ok(()) }