Add sled migration utility.
This commit is contained in:
parent
6b6be06c89
commit
5630b4ed20
|
@ -0,0 +1,37 @@
|
||||||
|
use tenebrous_dicebot::db::sqlite::{Database as SqliteDatabase, Variables};
|
||||||
|
use tenebrous_dicebot::db::Database;
|
||||||
|
use tenebrous_dicebot::error::BotError;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), BotError> {
|
||||||
|
let sled_path = std::env::args()
|
||||||
|
.skip(1)
|
||||||
|
.next()
|
||||||
|
.expect("Need a path to a Sled database as an arument.");
|
||||||
|
|
||||||
|
let sqlite_path = std::env::args()
|
||||||
|
.skip(2)
|
||||||
|
.next()
|
||||||
|
.expect("Need a path to an sqlite database as an arument.");
|
||||||
|
|
||||||
|
let db = Database::new(&sled_path)?;
|
||||||
|
|
||||||
|
let all_variables = db.variables.get_all_variables()?;
|
||||||
|
|
||||||
|
let sql_db = SqliteDatabase::new(&sqlite_path).await?;
|
||||||
|
|
||||||
|
for var in all_variables {
|
||||||
|
if let ((username, room_id, variable_name), value) = var {
|
||||||
|
println!(
|
||||||
|
"Migrating {}::{}::{} = {} to sql",
|
||||||
|
username, room_id, variable_name, value
|
||||||
|
);
|
||||||
|
|
||||||
|
sql_db
|
||||||
|
.set_user_variable(&username, &room_id, &variable_name, value)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ pub mod variables;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
db: Db,
|
db: Db,
|
||||||
pub(crate) variables: Variables,
|
pub variables: Variables,
|
||||||
pub(crate) migrations: Migrations,
|
pub(crate) migrations: Migrations,
|
||||||
pub(crate) rooms: Rooms,
|
pub(crate) rooms: Rooms,
|
||||||
pub(crate) state: DbState,
|
pub(crate) state: DbState,
|
||||||
|
|
|
@ -26,6 +26,9 @@ pub enum DataError {
|
||||||
#[error("expected i32, but i32 schema was violated")]
|
#[error("expected i32, but i32 schema was violated")]
|
||||||
I32SchemaViolation,
|
I32SchemaViolation,
|
||||||
|
|
||||||
|
#[error("parse error")]
|
||||||
|
ParseError(#[from] std::num::ParseIntError),
|
||||||
|
|
||||||
#[error("unexpected or corruptd data bytes")]
|
#[error("unexpected or corruptd data bytes")]
|
||||||
InvalidValue,
|
InvalidValue,
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub(crate) trait Rooms {
|
||||||
// TODO move this up to the top once we delete sled. Traits will be the
|
// TODO move this up to the top once we delete sled. Traits will be the
|
||||||
// main API, then we can have different impls for different DBs.
|
// main API, then we can have different impls for different DBs.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub(crate) trait Variables {
|
pub trait Variables {
|
||||||
async fn get_user_variables(
|
async fn get_user_variables(
|
||||||
&self,
|
&self,
|
||||||
user: &str,
|
user: &str,
|
||||||
|
|
|
@ -10,6 +10,8 @@ use std::str;
|
||||||
use zerocopy::byteorder::I32;
|
use zerocopy::byteorder::I32;
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
|
use super::errors;
|
||||||
|
|
||||||
pub(super) mod migrations;
|
pub(super) mod migrations;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -67,6 +69,9 @@ fn alter_room_variable_count(
|
||||||
Ok(new_count)
|
Ok(new_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Room ID, Username, Variable Name
|
||||||
|
pub type AllVariablesKey = (String, String, String);
|
||||||
|
|
||||||
impl Variables {
|
impl Variables {
|
||||||
pub(in crate::db) fn new(db: &sled::Db) -> Result<Variables, sled::Error> {
|
pub(in crate::db) fn new(db: &sled::Db) -> Result<Variables, sled::Error> {
|
||||||
Ok(Variables {
|
Ok(Variables {
|
||||||
|
@ -75,6 +80,40 @@ impl Variables {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_all_variables(&self) -> Result<HashMap<AllVariablesKey, i32>, DataError> {
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
let variables: Result<Vec<(AllVariablesKey, i32)>, DataError> = self
|
||||||
|
.room_user_variables
|
||||||
|
.scan_prefix("")
|
||||||
|
.map(|entry| match entry {
|
||||||
|
Ok((key, raw_value)) => {
|
||||||
|
let keys: Vec<_> = key
|
||||||
|
.split(|&b| b == 0xfe || b == 0xff)
|
||||||
|
.map(|b| str::from_utf8(b))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let &[Ok(room_id), Ok(username), Ok(variable_name), ..] = keys.as_slice() {
|
||||||
|
Ok((
|
||||||
|
(
|
||||||
|
room_id.to_owned(),
|
||||||
|
username.to_owned(),
|
||||||
|
variable_name.to_owned(),
|
||||||
|
),
|
||||||
|
convert_i32(&raw_value)?,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(errors::DataError::InvalidValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Convert tuples to hash map with collect(), inferred via
|
||||||
|
// return type.
|
||||||
|
variables.map(|entries| entries.into_iter().collect())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_user_variables(
|
pub fn get_user_variables(
|
||||||
&self,
|
&self,
|
||||||
key: &UserAndRoom<'_>,
|
key: &UserAndRoom<'_>,
|
||||||
|
|
Loading…
Reference in New Issue