From c7121a906c982f5a557b50897ac71f3b64128472 Mon Sep 17 00:00:00 2001 From: onenewcode Date: Thu, 27 Aug 2026 15:39:03 +0800 Subject: [PATCH] Support invoking sea-orm-cli as a Cargo subcommand --- sea-orm-cli/Cargo.toml | 5 ++++ sea-orm-cli/README.md | 11 ++++++++- sea-orm-cli/src/cli.rs | 54 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index f315f350db..06cac89cc8 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -41,6 +41,11 @@ name = "sea" path = "src/bin/main.rs" required-features = ["cli", "codegen"] +[[bin]] +name = "cargo-sea" +path = "src/bin/main.rs" +required-features = ["cli", "codegen"] + [dependencies] async-std = { version = "1.9", default-features = false, features = [ "attributes", diff --git a/sea-orm-cli/README.md b/sea-orm-cli/README.md index 880512bdff..d052617ef8 100644 --- a/sea-orm-cli/README.md +++ b/sea-orm-cli/README.md @@ -14,6 +14,16 @@ Or: > sea help ``` +### As a Cargo subcommand + +`cargo install sea-orm-cli` also installs a `cargo-sea` binary, so the CLI can be invoked +as a standard Cargo subcommand: + +```sh +> cargo sea generate entity -u sqlite://bakery.db -o out +> cargo sea migrate up +``` + Getting Help: ```sh @@ -78,4 +88,3 @@ cargo run -- generate entity -u postgres://sea:sea@localhost/bakery -s public -o ```sh cargo run -- migrate status ``` - diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 4f0baf8323..07340e5b6a 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -1,7 +1,8 @@ use clap::{ArgAction, ArgGroup, Parser, Subcommand, ValueEnum}; #[cfg(feature = "codegen")] use dotenvy::dotenv; -use std::ffi::OsStr; +use std::ffi::{OsStr, OsString}; +use std::path::Path; #[cfg(feature = "codegen")] use crate::{handle_error, run_generate_command, run_migrate_command}; @@ -417,17 +418,38 @@ fn is_deprecated_preserve_user_modifications_flag(arg: &OsStr) -> bool { .is_some_and(|arg| arg.starts_with("--preserve-user-modifications")) } +/// As a Cargo subcommand, `cargo sea …` runs `cargo-sea` with `sea` injected as argv[1]. +/// Strip it (only under a `cargo-` binary name) so clap sees the real arguments. +fn strip_cargo_subcommand(mut args: Vec) -> Vec { + let sub = args + .first() + .map(Path::new) + .and_then(Path::file_stem) // strips the `.exe` suffix on Windows + .and_then(OsStr::to_str) + .and_then(|name| name.strip_prefix("cargo-")) + .map(str::to_owned); + if let Some(sub) = sub { + if args.get(1).map(OsString::as_os_str) == Some(OsStr::new(&sub)) { + args.remove(1); + } + } + args +} + /// Use this to build a local, version-controlled `sea-orm-cli` in dependent projects /// (see [example use case](https://github.com/SeaQL/sea-orm/discussions/1889)). #[cfg(feature = "codegen")] pub async fn main() { dotenv().ok(); - let deprecated_preserve_user_modifications_flag_used = std::env::args_os() + let args = strip_cargo_subcommand(std::env::args_os().collect()); + + let deprecated_preserve_user_modifications_flag_used = args + .iter() .skip(1) - .any(|arg| is_deprecated_preserve_user_modifications_flag(&arg)); + .any(|arg| is_deprecated_preserve_user_modifications_flag(arg)); - let cli = Cli::parse(); + let cli = Cli::parse_from(&args); if deprecated_preserve_user_modifications_flag_used { eprintln!( "warning: `--preserve-user-modifications` is deprecated; use `--experimental-preserve-user-modifications` instead." @@ -456,3 +478,27 @@ pub async fn main() { .unwrap_or_else(handle_error), } } + +#[cfg(test)] +mod tests { + use super::*; + + fn strip(args: &[&str]) -> Vec { + strip_cargo_subcommand(args.iter().map(OsString::from).collect()) + } + + #[test] + fn strip_cargo_subcommand_only_strips_injected_name() { + // `cargo sea …` runs `cargo-sea sea …`; the injected `sea` is removed. + assert_eq!( + strip(&["cargo-sea", "sea", "generate"]), + ["cargo-sea", "generate"] + ); + // Standalone binaries and non-matching argv[1] are left untouched. + assert_eq!( + strip(&["sea", "sea", "generate"]), + ["sea", "sea", "generate"] + ); + assert_eq!(strip(&["cargo-sea", "generate"]), ["cargo-sea", "generate"]); + } +}