Skip to content

Commit

Permalink
Worked on LibSQL drivers
Browse files Browse the repository at this point in the history
  • Loading branch information
emilpriver committed Dec 13, 2023
1 parent cef4f5f commit 4c6cecd
Show file tree
Hide file tree
Showing 8 changed files with 2,481 additions and 183 deletions.
2,475 changes: 2,311 additions & 164 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ edition = "2021"

[dependencies]
anyhow = "1.0.75"
async-trait = "0.1.74"
chrono = { version = "0.4.31", features = ["serde"] }
clap = { version = "4.4.11", features = ["env", "string", "derive", "cargo"] }
libsql-client = { version = "0.33.2", features = ["futures-util"] }

[dev-dependencies]
tempfile = "3.8.1"
9 changes: 9 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::{bail, Result};
use std::env;

pub fn migration_folder() -> String {
Expand All @@ -7,3 +8,11 @@ pub fn migration_folder() -> String {

"./migrations".to_string()
}

pub fn database_url() -> Result<String> {
if let Ok(v) = env::var("DATABASE_URL") {
return Ok(v);
}

bail!("missing DATABASE_URL env variable")
}
15 changes: 15 additions & 0 deletions src/database.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use anyhow::{bail, Result};
use async_trait::async_trait;

#[async_trait]
pub trait DatabaseDriver {
async fn execute(self, content: &str) -> Result<()>;
}

pub fn new(db_url: &str) -> Result<impl DatabaseDriver> {
if db_url.starts_with("libsql") {
// TODO: return LibSQL database client
}

bail!("No matching database")
}
45 changes: 43 additions & 2 deletions src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ pub fn generate_new_migration(migration_name: &str) -> Result<()> {

for f in file_endings {
let filename = format!("{migration_path}/{timestamp}_{name}.{f}.sql");
let filename_str = &filename.as_str();
let filename_str = filename.as_str();
let path = std::path::Path::new(filename_str);

// Generate the folder id it don't exist
// Generate the folder if it don't exist
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
Expand All @@ -32,7 +32,48 @@ pub fn generate_new_migration(migration_name: &str) -> Result<()> {
Err(err) => bail!(err),
_ => {}
};

println!("Generated {}", filename_str)
}

Ok(())
}

#[cfg(test)]
mod tests {

use super::*;
use std::env;
use std::fs;
use tempfile::tempdir;

#[test]
fn test_generate_migration() {
let migration_name = "Create users table";

let tmp_dir = tempdir().unwrap();
let migration_folder = tmp_dir.path();
let migration_folder_string = migration_folder.to_str().unwrap();

env::set_var("MIGRATION_DIR", migration_folder_string);

let result = generate_new_migration(migration_name);

assert!(result.is_ok());

let timestamp = Utc::now().timestamp();
let name = migration_name.replace(" ", "_").to_lowercase();

let up_file = format!("{migration_folder_string}/{timestamp}_{name}.up.sql");
let down_file = format!("{migration_folder_string}/{timestamp}_{name}.down.sql");

assert!(fs::metadata(&up_file).is_ok());
assert!(fs::metadata(&down_file).is_ok());

let up_contents = fs::read_to_string(&up_file).unwrap();
assert!(up_contents.contains("Write your up sql migration here"));

let down_contents = fs::read_to_string(&down_file).unwrap();
assert!(down_contents.contains("Write your down sql migration here"));
}
}
36 changes: 36 additions & 0 deletions src/libsql_driver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::database::DatabaseDriver;
use anyhow::{bail, Result};
use async_trait::async_trait;
use libsql_client::{
http::{Client, InnerClient},
Config,
};

struct LibSQLDriver {
db: Client,
}

impl LibSQLDriver {
fn new(db_url: &str, token: &str) -> Result<LibSQLDriver> {
let inner_client = InnerClient::Default;
let config = Config::new(db_url)?.with_auth_token(token);

let client = match Client::from_config(inner_client, config) {
Ok(c) => c,
Err(err) => bail!("{:?}", err),
};

Ok(LibSQLDriver { db: client })
}
}

#[async_trait]
impl DatabaseDriver for LibSQLDriver {
// execute query with the provided database
async fn execute(self, content: &str) -> Result<()> {
match self.db.execute(content).await {
Ok(..) => Ok(()),
Err(err) => bail!("{:?}", err),
}
}
}
40 changes: 23 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use clap::{Arg, ArgAction, Command};

use crate::generate::generate_new_migration;

mod config;
mod database;
mod generate;
mod libsql_driver;
mod migrate;

fn main() {
let matches = Command::new("libsql-migeate")
Expand All @@ -12,28 +13,33 @@ fn main() {
.subcommand_required(true)
.arg_required_else_help(true)
.author("Emil Privér")
.subcommand(
Command::new("new")
.short_flag('n')
.long_flag("new")
.about("Create new migration")
.arg(
Arg::new("name")
.short('n')
.long("name")
.help("Name for your new migration")
.action(ArgAction::Set)
.num_args(1),
),
)
.subcommands([
Command::new("new").about("Create new migration").arg(
Arg::new("name")
.short('n')
.long("name")
.help("Name for your new migration")
.action(ArgAction::Set)
.num_args(1),
),
Command::new("up").about("Migrate to the latest version"),
])
.get_matches();

match matches.subcommand() {
Some(("new", query_matches)) => {
let name = query_matches.get_one::<String>("name").unwrap();
let new = generate_new_migration(name);
let new = generate::generate_new_migration(name);
println!("{:?}", &new);
}
Some(("up", _)) => {
match migrate::up() {
Err(err) => {
println!("{:?}", err)
}
_ => {}
};
}
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachable
}
}
39 changes: 39 additions & 0 deletions src/migrate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::config::migration_folder;
use anyhow::{bail, Result};
use std::fs;
use std::path::PathBuf;

fn get_paths(folder: &PathBuf, ending: &str) -> Vec<PathBuf> {
let entries = fs::read_dir(folder).unwrap();

let mut migration_files = vec![];

for entry in entries {
let entry = entry.unwrap();
let path = entry.path();

if path.extension().unwrap().to_str().unwrap() == format!("{ending}.sql") {
migration_files.push(path);
}
}

migration_files
}

pub fn up() -> Result<()> {
let folder = migration_folder();
let path = PathBuf::from(&folder);
let files = get_paths(&path, "up");

if files.len() == 0 {
bail!(
"Didn't find any files ending with .up.sql at {}. Does the path exist?",
folder
);
}

for f in files {}

Ok(())
}

0 comments on commit 4c6cecd

Please sign in to comment.