Skip to content

Commit fef3778

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/main'
2 parents 8b24838 + 453701c commit fef3778

File tree

4 files changed

+92
-2
lines changed

4 files changed

+92
-2
lines changed

examples/official-site/extensions-to-sql.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ SQLPage implements variables in the following way:
1919

2020
When sending a POST request, most often by sending a form with the
2121
[form component](/component.sql?component=form), the form data is made
22-
available as variables prefixed by a semi-colon.
22+
available as variables prefixed by a colon.
2323

2424
So when this form is sent:
2525

sqlpage/migrations/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ that is greater than the previous one.
2121
Use commands like `ALTER TABLE` to update the schema declaratively instead of modifying the existing `CREATE TABLE`
2222
statements.
2323

24-
If you try to edit an existing migration, SQLPage will not run it again, will detect
24+
If you try to edit an existing migration, SQLPage will not run it again, it will detect that the migration has already executed. Also, if the migration is different than the one that was executed, SQLPage will throw an error as the database structure must match.
25+
26+
## Creating migrations on the command line
27+
28+
You can create a migration directly with sqlpage by running the command `sqlpage create-migration [migration_name]`
29+
30+
For example if you run `sqlpage create-migration "Example Migration 1"` on the command line, you will find a new file under the `sqlpage/migrations` folder called `[timestamp]_example_migration_1.sql` where timestamp is the current time when you ran the command.
2531

2632
## Running migrations
2733

src/app_config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ pub struct Cli {
2222
/// The path to the configuration file.
2323
#[clap(short = 'c', long)]
2424
pub config_file: Option<PathBuf>,
25+
26+
/// Subcommands for additional functionality.
27+
#[clap(subcommand)]
28+
pub command: Option<Commands>,
29+
}
30+
31+
/// Enum for subcommands.
32+
#[derive(Parser)]
33+
pub enum Commands {
34+
/// Create a new migration file.
35+
CreateMigration {
36+
/// Name of the migration.
37+
migration_name: String,
38+
},
2539
}
2640

2741
#[cfg(not(feature = "lambda-web"))]
@@ -686,6 +700,7 @@ mod test {
686700
web_root: Some(PathBuf::from(".")),
687701
config_dir: None,
688702
config_file: None,
703+
command: None,
689704
};
690705

691706
let config = AppConfig::from_cli(&cli).unwrap();
@@ -726,6 +741,7 @@ mod test {
726741
web_root: None,
727742
config_dir: None,
728743
config_file: Some(config_file_path.clone()),
744+
command: None,
729745
};
730746

731747
let config = AppConfig::from_cli(&cli).unwrap();
@@ -744,6 +760,7 @@ mod test {
744760
web_root: Some(cli_web_dir.clone()),
745761
config_dir: None,
746762
config_file: Some(config_file_path),
763+
command: None,
747764
};
748765

749766
let config = AppConfig::from_cli(&cli_with_web_root).unwrap();
@@ -773,6 +790,7 @@ mod test {
773790
web_root: None,
774791
config_dir: None,
775792
config_file: None,
793+
command: None,
776794
};
777795

778796
let config = AppConfig::from_cli(&cli).unwrap();

src/main.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use clap::Parser;
12
use sqlpage::{
23
app_config,
34
webserver::{self, Database},
@@ -15,9 +16,25 @@ async fn main() {
1516

1617
async fn start() -> anyhow::Result<()> {
1718
let app_config = app_config::load_from_cli()?;
19+
let cli = app_config::Cli::parse();
20+
21+
if let Some(command) = cli.command {
22+
match command {
23+
app_config::Commands::CreateMigration { migration_name } => {
24+
// Pass configuration_directory from app_config
25+
create_migration_file(
26+
&migration_name,
27+
app_config.configuration_directory.to_str().unwrap(),
28+
)?;
29+
return Ok(());
30+
}
31+
}
32+
}
33+
1834
let db = Database::init(&app_config).await?;
1935
webserver::database::migrations::apply(&app_config, &db).await?;
2036
let state = AppState::init_with_db(&app_config, db).await?;
37+
2138
log::debug!("Starting server...");
2239
webserver::http::run_server(&app_config, state).await?;
2340
log::info!("Server stopped gracefully. Goodbye!");
@@ -41,3 +58,52 @@ fn init_logging() {
4158
Err(e) => log::error!("Error loading .env file: {e}"),
4259
}
4360
}
61+
62+
fn create_migration_file(
63+
migration_name: &str,
64+
configuration_directory: &str,
65+
) -> anyhow::Result<()> {
66+
use chrono::Utc;
67+
use std::fs;
68+
use std::path::Path;
69+
70+
let timestamp = Utc::now().format("%Y%m%d%H%M%S").to_string();
71+
let snake_case_name = migration_name
72+
.replace(|c: char| !c.is_alphanumeric(), "_")
73+
.to_lowercase();
74+
let file_name = format!("{}_{}.sql", timestamp, snake_case_name);
75+
let migrations_dir = Path::new(configuration_directory).join("migrations");
76+
77+
if !migrations_dir.exists() {
78+
fs::create_dir_all(&migrations_dir)?;
79+
}
80+
81+
let mut unique_file_name = file_name.clone();
82+
let mut counter = 1;
83+
84+
while migrations_dir.join(&unique_file_name).exists() {
85+
unique_file_name = format!("{}_{}_{}.sql", timestamp, snake_case_name, counter);
86+
counter += 1;
87+
}
88+
89+
let file_path = migrations_dir.join(unique_file_name);
90+
fs::write(&file_path, "-- Write your migration here\n")?;
91+
92+
// the following code cleans up the display path to show where the migration was created
93+
// relative to the current working directory, and then outputs the path to the migration
94+
let file_path_canon = file_path.canonicalize().unwrap_or(file_path.clone());
95+
let cwd_canon = std::env::current_dir()?
96+
.canonicalize()
97+
.unwrap_or(std::env::current_dir()?);
98+
let rel_path = match file_path_canon.strip_prefix(&cwd_canon) {
99+
Ok(p) => p,
100+
Err(_) => file_path_canon.as_path(),
101+
};
102+
let mut display_path_str = rel_path.display().to_string();
103+
if display_path_str.starts_with("\\\\?\\") {
104+
display_path_str = display_path_str.trim_start_matches("\\\\?\\").to_string();
105+
}
106+
display_path_str = display_path_str.replace('\\', "/");
107+
println!("Migration file created: {}", display_path_str);
108+
Ok(())
109+
}

0 commit comments

Comments
 (0)