mirror of
https://codeberg.org/icewind/nextcloud-config-parser.git
synced 2026-06-03 08:34:13 +02:00
prepare for updated sqlx
This commit is contained in:
parent
882b907141
commit
25721b7f98
3 changed files with 259 additions and 146 deletions
17
Cargo.toml
17
Cargo.toml
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "nextcloud-config-parser"
|
||||
description = "Rust parser for nextcloud config files"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
|
@ -10,15 +10,18 @@ documentation = "https://docs.rs/nextcloud-config-parser"
|
|||
rust-version = "1.58"
|
||||
|
||||
[dependencies]
|
||||
redis = { version = "0.23.0", optional = true, default-features = false }
|
||||
thiserror = "1.0.40"
|
||||
redis = { version = "0.23.3", optional = true, default-features = false }
|
||||
thiserror = "1.0.50"
|
||||
php-literal-parser = "0.5.1"
|
||||
sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "any", "mysql", "sqlite", "postgres"], optional = true }
|
||||
miette = "5.8.0"
|
||||
sqlx = { version = "0.7.3", default-features = false, features = ["runtime-tokio-rustls", "any", "mysql", "sqlite", "postgres"], optional = true }
|
||||
miette = "5.10.0"
|
||||
futures-core = "0.3.29"
|
||||
urlencoding = "2.1.3"
|
||||
form_urlencoded = "1.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "any", "mysql", "sqlite", "postgres"] }
|
||||
miette = { version = "5.8.0", features = ["fancy"] }
|
||||
sqlx = { version = "0.7.1", default-features = false, features = ["runtime-tokio-rustls", "any", "mysql", "sqlite", "postgres"] }
|
||||
miette = { version = "5.10.0", features = ["fancy"] }
|
||||
|
||||
[features]
|
||||
db-sqlx = ["sqlx"]
|
||||
|
|
|
|||
156
src/lib.rs
156
src/lib.rs
|
|
@ -1,5 +1,6 @@
|
|||
mod nc;
|
||||
|
||||
use form_urlencoded::Serializer;
|
||||
use miette::Diagnostic;
|
||||
#[cfg(feature = "redis-connect")]
|
||||
use redis::{ConnectionAddr, ConnectionInfo};
|
||||
|
|
@ -120,7 +121,7 @@ pub enum NotAConfigError {
|
|||
NotAnArray(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SslOptions {
|
||||
Enabled {
|
||||
key: String,
|
||||
|
|
@ -132,7 +133,7 @@ pub enum SslOptions {
|
|||
Default,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Database {
|
||||
Sqlite {
|
||||
database: PathBuf,
|
||||
|
|
@ -153,24 +154,17 @@ pub enum Database {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DbConnect {
|
||||
Tcp { host: String, port: u16 },
|
||||
Socket(PathBuf),
|
||||
}
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
impl From<Database> for sqlx::any::AnyConnectOptions {
|
||||
fn from(cfg: Database) -> Self {
|
||||
use sqlx::{
|
||||
mysql::{MySqlConnectOptions, MySqlSslMode},
|
||||
postgres::{PgConnectOptions, PgSslMode},
|
||||
sqlite::SqliteConnectOptions,
|
||||
};
|
||||
|
||||
match cfg {
|
||||
impl Database {
|
||||
pub fn url<'a>(&self) -> String {
|
||||
match self {
|
||||
Database::Sqlite { database } => {
|
||||
SqliteConnectOptions::default().filename(database).into()
|
||||
format!("sqlite://{}", database.display())
|
||||
}
|
||||
Database::MySql {
|
||||
database,
|
||||
|
|
@ -179,34 +173,57 @@ impl From<Database> for sqlx::any::AnyConnectOptions {
|
|||
connect,
|
||||
ssl_options,
|
||||
} => {
|
||||
let mut options = MySqlConnectOptions::default()
|
||||
.database(&database)
|
||||
.username(&username)
|
||||
.password(&password);
|
||||
let mut params = Serializer::new(String::new());
|
||||
match ssl_options {
|
||||
SslOptions::Enabled { ca, verify, .. } => {
|
||||
options = options.ssl_ca(ca);
|
||||
options = options.ssl_mode(if verify {
|
||||
MySqlSslMode::VerifyIdentity
|
||||
} else {
|
||||
MySqlSslMode::VerifyCa
|
||||
});
|
||||
}
|
||||
SslOptions::Disabled => {
|
||||
options = options.ssl_mode(MySqlSslMode::Disabled);
|
||||
}
|
||||
SslOptions::Default => {}
|
||||
SslOptions::Disabled => {
|
||||
params.append_pair("ssl-mode", "disabled");
|
||||
}
|
||||
match connect {
|
||||
SslOptions::Enabled { ca, verify, .. } => {
|
||||
params.append_pair(
|
||||
"ssl-mode",
|
||||
if *verify {
|
||||
"verify_identity"
|
||||
} else {
|
||||
"verify_ca"
|
||||
},
|
||||
);
|
||||
params.append_pair("ssl-ca", ca.as_str());
|
||||
}
|
||||
}
|
||||
let (host, port) = match connect {
|
||||
DbConnect::Socket(socket) => {
|
||||
options = options.socket(socket);
|
||||
params.append_pair("socket", &socket.to_string_lossy());
|
||||
("localhost", 3306) // ignored when socket is set
|
||||
}
|
||||
DbConnect::Tcp { host, port } => {
|
||||
options = options.host(&host).port(port);
|
||||
DbConnect::Tcp { host, port } => (host.as_str(), *port),
|
||||
};
|
||||
let params = params.finish().replace("%2F", "/");
|
||||
let params_start = if params.is_empty() { "" } else { "?" };
|
||||
|
||||
if port == 3306 {
|
||||
format!(
|
||||
"mysql://{}:{}@{}/{}{}{}",
|
||||
urlencoding::encode(username),
|
||||
urlencoding::encode(password),
|
||||
host,
|
||||
database,
|
||||
params_start,
|
||||
params
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"mysql://{}:{}@{}:{}/{}{}{}",
|
||||
urlencoding::encode(username),
|
||||
urlencoding::encode(password),
|
||||
host,
|
||||
port,
|
||||
database,
|
||||
params_start,
|
||||
params
|
||||
)
|
||||
}
|
||||
}
|
||||
options.into()
|
||||
}
|
||||
Database::Postgres {
|
||||
database,
|
||||
username,
|
||||
|
|
@ -214,27 +231,64 @@ impl From<Database> for sqlx::any::AnyConnectOptions {
|
|||
connect,
|
||||
ssl_options,
|
||||
} => {
|
||||
let mut options = PgConnectOptions::default()
|
||||
.database(&database)
|
||||
.username(&username);
|
||||
|
||||
if !password.is_empty() {
|
||||
options = options.password(&password);
|
||||
let mut params = Serializer::new(String::new());
|
||||
match ssl_options {
|
||||
SslOptions::Default => {}
|
||||
SslOptions::Disabled => {
|
||||
params.append_pair("sslmode", "disable");
|
||||
}
|
||||
|
||||
if matches!(ssl_options, SslOptions::Disabled) {
|
||||
options = options.ssl_mode(PgSslMode::Disable);
|
||||
SslOptions::Enabled { ca, verify, .. } => {
|
||||
params.append_pair(
|
||||
"ssl-mode",
|
||||
if *verify { "verify-full" } else { "verify-ca" },
|
||||
);
|
||||
params.append_pair("sslrootcert", ca.as_str());
|
||||
}
|
||||
match connect {
|
||||
}
|
||||
let (host, port) = match connect {
|
||||
DbConnect::Socket(socket) => {
|
||||
options = options.socket(socket);
|
||||
params.append_pair("host", &socket.to_string_lossy());
|
||||
("localhost", 5432) // ignored when socket is set
|
||||
}
|
||||
DbConnect::Tcp { host, port } => {
|
||||
options = options.host(&host).port(port);
|
||||
}
|
||||
}
|
||||
options.into()
|
||||
DbConnect::Tcp { host, port } => (host.as_str(), *port),
|
||||
};
|
||||
let params = params.finish().replace("%2F", "/");
|
||||
let params_start = if params.is_empty() { "" } else { "?" };
|
||||
|
||||
if port == 5432 {
|
||||
format!(
|
||||
"postgresql://{}:{}@{}/{}{}{}",
|
||||
urlencoding::encode(username),
|
||||
urlencoding::encode(password),
|
||||
host,
|
||||
database,
|
||||
params_start,
|
||||
params
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"postgresql://{}:{}@{}:{}/{}{}{}",
|
||||
urlencoding::encode(username),
|
||||
urlencoding::encode(password),
|
||||
host,
|
||||
port,
|
||||
database,
|
||||
params_start,
|
||||
params
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
impl TryFrom<Database> for sqlx::any::AnyConnectOptions {
|
||||
type Error = sqlx::Error;
|
||||
|
||||
fn try_from(cfg: Database) -> Result<Self, Self::Error> {
|
||||
use std::str::FromStr;
|
||||
|
||||
sqlx::any::AnyConnectOptions::from_str(&cfg.url())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
184
tests/configs.rs
184
tests/configs.rs
|
|
@ -6,6 +6,10 @@ use nextcloud_config_parser::RedisConfig;
|
|||
#[cfg(feature = "redis-connect")]
|
||||
use redis::ConnectionInfo;
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
use sqlx::mysql::{MySqlConnectOptions, MySqlSslMode};
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
use sqlx::sqlite::SqliteConnectOptions;
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
use sqlx::{any::AnyConnectOptions, postgres::PgConnectOptions};
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
use std::str::FromStr;
|
||||
|
|
@ -39,14 +43,12 @@ fn test_parse_config_basic() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str(
|
||||
"mysql://nextcloud:secret@127.0.0.1/nextcloud?ssl-mode=disabled",
|
||||
)
|
||||
.unwrap(),
|
||||
config.database.into(),
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"mysql://nextcloud:secret@127.0.0.1/nextcloud?ssl-mode=disabled"
|
||||
);
|
||||
|
||||
#[cfg(feature = "redis-connect")]
|
||||
assert_debug_equal(
|
||||
RedisConfig::Single(ConnectionInfo::from_str("redis://127.0.0.1").unwrap()),
|
||||
|
|
@ -110,14 +112,12 @@ fn test_parse_comment_whitespace() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str(
|
||||
"mysql://nextcloud:secret@127.0.0.1/nextcloud?ssl-mode=disabled",
|
||||
)
|
||||
.unwrap(),
|
||||
config.database.into(),
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"mysql://nextcloud:secret@127.0.0.1/nextcloud?ssl-mode=disabled"
|
||||
);
|
||||
|
||||
#[cfg(feature = "redis-connect")]
|
||||
assert_debug_equal(
|
||||
RedisConfig::Single(ConnectionInfo::from_str("redis://127.0.0.1").unwrap()),
|
||||
|
|
@ -141,13 +141,10 @@ fn test_parse_port_in_host() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str(
|
||||
"mysql://nextcloud:secret@127.0.0.1:1234/nextcloud?ssl-mode=disabled",
|
||||
)
|
||||
.unwrap(),
|
||||
config.database.into(),
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"mysql://nextcloud:secret@127.0.0.1:1234/nextcloud?ssl-mode=disabled"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -164,16 +161,21 @@ fn test_parse_postgres_socket() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"postgresql://redacted:redacted@localhost/nextcloud?host=/var/run/postgresql"
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from(
|
||||
PgConnectOptions::new()
|
||||
.socket("/var/run/postgresql")
|
||||
.host("localhost")
|
||||
.username("redacted")
|
||||
.password("redacted")
|
||||
.database("nextcloud"),
|
||||
),
|
||||
config.database.into(),
|
||||
PgConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -190,15 +192,19 @@ fn test_parse_postgres_socket_no_pass() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"postgresql://redacted:@localhost/nextcloud?host=/var/run/postgresql"
|
||||
);
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from(
|
||||
PgConnectOptions::new()
|
||||
.socket("/var/run/postgresql")
|
||||
.host("localhost")
|
||||
.username("redacted")
|
||||
.database("nextcloud"),
|
||||
),
|
||||
config.database.into(),
|
||||
PgConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -215,16 +221,21 @@ fn test_parse_postgres_socket_folder() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"postgresql://redacted:redacted@localhost/nextcloud?host=/var/run/postgresql"
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from(
|
||||
PgConnectOptions::new()
|
||||
.socket("/var/run/postgresql")
|
||||
.host("localhost")
|
||||
.username("redacted")
|
||||
.password("redacted")
|
||||
.database("nextcloud"),
|
||||
),
|
||||
config.database.into(),
|
||||
PgConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -265,14 +276,12 @@ fn test_parse_config_multiple() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str(
|
||||
"mysql://nextcloud:secret@127.0.0.1/nextcloud?ssl-mode=disabled",
|
||||
)
|
||||
.unwrap(),
|
||||
config.database.into(),
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"mysql://nextcloud:secret@127.0.0.1/nextcloud?ssl-mode=disabled"
|
||||
);
|
||||
|
||||
#[cfg(feature = "redis-connect")]
|
||||
assert_debug_equal(
|
||||
RedisConfig::Single(ConnectionInfo::from_str("redis://127.0.0.1").unwrap()),
|
||||
|
|
@ -294,7 +303,7 @@ fn test_parse_config_multiple_no_glob() {
|
|||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str("sqlite:///nc/nextcloud.db").unwrap(),
|
||||
config.database.into(),
|
||||
config.database.try_into().unwrap(),
|
||||
);
|
||||
#[cfg(feature = "redis-connect")]
|
||||
assert_debug_equal(
|
||||
|
|
@ -319,13 +328,21 @@ fn test_parse_config_mysql_fqdn() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"mysql://nextcloud:secret@db.example.com/nextcloud"
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str(
|
||||
"mysql://nextcloud:secret@db.example.com/nextcloud?ssl-mode=preferred",
|
||||
)
|
||||
.unwrap(),
|
||||
config.database.into(),
|
||||
MySqlConnectOptions::new()
|
||||
.username("nextcloud")
|
||||
.password("secret")
|
||||
.database("nextcloud")
|
||||
.host("db.example.com")
|
||||
.ssl_mode(MySqlSslMode::Preferred),
|
||||
MySqlConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -345,13 +362,21 @@ fn test_parse_config_mysql_ip_no_verify() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"mysql://nextcloud:secret@1.2.3.4/nextcloud"
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str(
|
||||
"mysql://nextcloud:secret@1.2.3.4/nextcloud?ssl-mode=preferred",
|
||||
)
|
||||
.unwrap(),
|
||||
config.database.into(),
|
||||
MySqlConnectOptions::new()
|
||||
.username("nextcloud")
|
||||
.password("secret")
|
||||
.database("nextcloud")
|
||||
.host("1.2.3.4")
|
||||
.ssl_mode(MySqlSslMode::Preferred),
|
||||
MySqlConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -376,13 +401,22 @@ fn test_parse_config_mysql_ssl_ca() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"mysql://nextcloud:secret@db.example.com/nextcloud?ssl-mode=verify_identity&ssl-ca=/ca-cert.pem"
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str(
|
||||
"mysql://nextcloud:secret@db.example.com/nextcloud?ssl-mode=verify_identity&ssl-ca=/ca-cert.pem",
|
||||
)
|
||||
.unwrap(),
|
||||
config.database.into(),
|
||||
MySqlConnectOptions::new()
|
||||
.username("nextcloud")
|
||||
.password("secret")
|
||||
.database("nextcloud")
|
||||
.host("db.example.com")
|
||||
.ssl_mode(MySqlSslMode::VerifyIdentity)
|
||||
.ssl_ca("/ca-cert.pem"),
|
||||
MySqlConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -407,13 +441,21 @@ fn test_parse_config_mysql_ssl_ca_no_verify() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"mysql://nextcloud:secret@db.example.com/nextcloud?ssl-mode=verify_ca&ssl-ca=/ca-cert.pem"
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from_str(
|
||||
"mysql://nextcloud:secret@db.example.com/nextcloud?ssl-mode=verify_ca&ssl-ca=/ca-cert.pem",
|
||||
)
|
||||
.unwrap(),
|
||||
config.database.into(),
|
||||
MySqlConnectOptions::new()
|
||||
.username("nextcloud")
|
||||
.password("secret")
|
||||
.database("nextcloud")
|
||||
.host("db.example.com")
|
||||
.ssl_mode(MySqlSslMode::VerifyCa)
|
||||
.ssl_ca("/ca-cert.pem"),
|
||||
MySqlConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -433,17 +475,21 @@ fn test_parse_postgres_ip() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"postgresql://redacted:redacted@1.2.3.4/nextcloud?sslmode=disable"
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from(
|
||||
PgConnectOptions::new()
|
||||
.host("1.2.3.4")
|
||||
.username("redacted")
|
||||
.password("redacted")
|
||||
.database("nextcloud")
|
||||
.port(5432)
|
||||
.ssl_mode(sqlx::postgres::PgSslMode::Disable),
|
||||
),
|
||||
config.database.into(),
|
||||
PgConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -463,16 +509,20 @@ fn test_parse_postgres_fqdn() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
assert_eq!(
|
||||
config.database.url(),
|
||||
"postgresql://redacted:redacted@pg.example.com/nextcloud"
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
AnyConnectOptions::from(
|
||||
PgConnectOptions::new()
|
||||
.host("pg.example.com")
|
||||
.username("redacted")
|
||||
.password("redacted")
|
||||
.port(5432)
|
||||
.database("nextcloud"),
|
||||
),
|
||||
config.database.into(),
|
||||
PgConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -485,6 +535,12 @@ fn test_parse_config_sqlite_default_db() {
|
|||
},
|
||||
&config.database,
|
||||
);
|
||||
|
||||
#[cfg(feature = "db-sqlx")]
|
||||
assert_debug_equal(
|
||||
SqliteConnectOptions::new().filename("/nc/data/owncloud.db"),
|
||||
SqliteConnectOptions::from_str(&config.database.url()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue