Add sled migration utility.

This commit is contained in:
projectmoon 2021-05-20 15:30:44 +00:00
parent 6b6be06c89
commit 5630b4ed20
5 changed files with 81 additions and 2 deletions

37
src/bin/migrate_sled.rs Normal file
View File

@ -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(())
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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<'_>,