use std::fs::{File, OpenOptions, create_dir}; use std::io::Write; use std::path::{Path, PathBuf}; use anyhow::{Result, anyhow}; use crate::compose::Compose; use crate::ctx::Ctx; use crate::scripts::docker_compose; const DEBUG_TEMPLATE: &str = r#"from django.core.management.base import BaseCommand class Command(BaseCommand): def handle(self, *args, **options): pass "#; pub fn make_command(app: &PathBuf, name: &str) -> Result<()> { let app_name = app.to_string_lossy(); let app_dir = Path::new(&app); let not_app_exists = !app_dir.is_dir(); if not_app_exists { return Err(anyhow!("directory {app_name} does not exist")); } eprintln!("found app {app_name}"); let management_dir = app_dir.join("management"); let not_management_exists = !management_dir.exists(); if not_management_exists { create_dir(&management_dir)?; create_file(management_dir.join("__init__.py"))?; eprintln!("created module {app_name}.management") }; let commands_dir = management_dir.join("commands"); let not_commands_exists = !commands_dir.exists(); if not_commands_exists { create_dir(&commands_dir)?; create_file(commands_dir.join("__init__.py"))?; eprintln!("created module {app_name}.management.commands") }; let mut file = safe_create_file(commands_dir.join(format!("{name}.py")))?; file.write_all(DEBUG_TEMPLATE.as_bytes())?; eprintln!("created command {app_name}.management.commands.{name}"); Ok(()) } pub fn bash(ctx: &Ctx) -> Result<()> { run(ctx, &["bash".to_string()]) } pub fn run(ctx: &Ctx, rest: &[String]) -> Result<()> { let service = Compose::resolve(ctx)?.django()?; docker_compose::run(ctx, &service, rest) } pub fn manage(ctx: &Ctx, rest: &[String]) -> Result<()> { let mut args = vec!["python".to_string(), "manage.py".to_string()]; args.extend_from_slice(rest); run(ctx, &args) } // shortcuts pub fn makemigrations(ctx: &Ctx) -> Result<()> { manage(ctx, &["makemigrations".to_string()]) } pub fn migrate(ctx: &Ctx, rest: &[String]) -> Result<()> { let mut full_rest = vec!["migrate".to_string()]; full_rest.extend_from_slice(rest); manage(ctx, &full_rest) } pub fn shell(ctx: &Ctx) -> Result<()> { manage(ctx, &["shell".to_string()]) } pub fn test(ctx: &Ctx) -> Result<()> { manage(ctx, &["test".to_string()]) } fn safe_create_file(path: PathBuf) -> Result { OpenOptions::new().write(true).create_new(true).open(path) } // truncate(false) keeps an existing file's contents, which is what callers creating // an empty __init__.py want fn create_file(path: PathBuf) -> Result { OpenOptions::new() .write(true) .create(true) .truncate(false) .open(path) }