fix: report what happened instead of losing it
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
@@ -16,9 +16,20 @@ class Command(BaseCommand):
|
||||
|
||||
"#;
|
||||
|
||||
pub fn make_command(ctx: &Ctx, app: &PathBuf, name: &str) -> Result<()> {
|
||||
pub fn make_command(ctx: &Ctx, app: &Path, name: &str) -> Result<()> {
|
||||
let app_name = app.to_string_lossy();
|
||||
let app_dir = Path::new(&app);
|
||||
let app_dir = app;
|
||||
|
||||
// it becomes `<name>.py` under the app, and django imports it by this name,
|
||||
// so anything that is not an identifier would land the file somewhere else
|
||||
// or somewhere django will never look for it
|
||||
if !is_module_name(name) {
|
||||
return Err(anyhow!(
|
||||
"{name:?} is not a usable command name; \
|
||||
django imports it as a python module, so it can hold only \
|
||||
letters, digits and underscores"
|
||||
));
|
||||
}
|
||||
|
||||
if !app_dir.is_dir() {
|
||||
return Err(anyhow!("directory {app_name} does not exist"));
|
||||
@@ -54,16 +65,30 @@ pub fn make_command(ctx: &Ctx, app: &PathBuf, name: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_module_name(name: &str) -> bool {
|
||||
!name.is_empty()
|
||||
&& !name.starts_with(|c: char| c.is_ascii_digit())
|
||||
&& name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
|
||||
}
|
||||
|
||||
pub fn bash(ctx: &Ctx) -> Result<()> {
|
||||
Bash.in_service(&service(ctx)?).replace(ctx)
|
||||
Bash.in_service(&service(ctx)?)
|
||||
.quiet(ctx.quiet)
|
||||
.replace(ctx)
|
||||
}
|
||||
|
||||
pub fn run(ctx: &Ctx, rest: &[String]) -> Result<()> {
|
||||
Words::new(rest).in_service(&service(ctx)?).replace(ctx)
|
||||
Words::new(rest)
|
||||
.in_service(&service(ctx)?)
|
||||
.quiet(ctx.quiet)
|
||||
.replace(ctx)
|
||||
}
|
||||
|
||||
pub fn manage(ctx: &Ctx, rest: &[String]) -> Result<()> {
|
||||
Manage::new(rest).in_service(&service(ctx)?).replace(ctx)
|
||||
Manage::new(rest)
|
||||
.in_service(&service(ctx)?)
|
||||
.quiet(ctx.quiet)
|
||||
.replace(ctx)
|
||||
}
|
||||
|
||||
// shortcuts
|
||||
@@ -85,3 +110,20 @@ pub fn shell(ctx: &Ctx) -> Result<()> {
|
||||
fn service(ctx: &Ctx) -> Result<String> {
|
||||
Project::resolve(ctx)?.django()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::is_module_name;
|
||||
|
||||
#[test]
|
||||
fn a_command_name_has_to_be_a_python_module_name() {
|
||||
for name in ["report", "send_mail", "_private", "sync2"] {
|
||||
assert!(is_module_name(name), "should be usable: {name}");
|
||||
}
|
||||
|
||||
// a path would put the file somewhere other than the app
|
||||
for name in ["../../../etc/cron.d/x", "a/b", "with space", "dash-ed", ""] {
|
||||
assert!(!is_module_name(name), "should be refused: {name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub fn add(ctx: &Ctx, paths: &[PathBuf], force: bool, store: Option<&Path>) -> R
|
||||
let mut failed = 0;
|
||||
for path in paths {
|
||||
if let Err(e) = link_one(ctx, &repo, path, force, &report) {
|
||||
note!(ctx, "error: {e:#}");
|
||||
warning!("{e:#}");
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
@@ -44,11 +44,11 @@ pub fn restore(ctx: &Ctx, paths: &[PathBuf], all: bool, store: Option<&Path>) ->
|
||||
let repo = Repo::discover(ctx, store)?;
|
||||
let report = Report::new(&repo);
|
||||
|
||||
let stored = match (all, paths) {
|
||||
(true, []) => stored_paths(&repo, &repo.store)?,
|
||||
(true, _) => bail!("--all restores everything, so it takes no paths"),
|
||||
(false, []) => bail!("name a path to restore, or pass --all"),
|
||||
(false, paths) => paths.iter().cloned().map(|p| (p, Stored::Linked)).collect(),
|
||||
// clap requires one or the other and refuses both, so only the two real
|
||||
// cases are left here
|
||||
let stored = match all {
|
||||
true => stored_paths(&repo, &repo.store)?,
|
||||
false => paths.iter().cloned().map(|p| (p, Stored::Linked)).collect(),
|
||||
};
|
||||
|
||||
// only a linked path can be moved back; the store can hold orphans too
|
||||
@@ -88,7 +88,7 @@ pub fn restore(ctx: &Ctx, paths: &[PathBuf], all: bool, store: Option<&Path>) ->
|
||||
let mut failed = 0;
|
||||
for path in &linked {
|
||||
if let Err(e) = restore_one(ctx, &repo, path, &report) {
|
||||
note!(ctx, "error: {e:#}");
|
||||
warning!("{e:#}");
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user