11use std:: ffi:: OsString ;
2+ use std:: fmt:: { self , Display , Formatter } ;
23use std:: ops:: { Deref , DerefMut } ;
34use std:: path:: PathBuf ;
45use std:: str:: FromStr ;
56
67use anyhow:: { Result , anyhow} ;
7- use clap:: builder :: Styles ;
8+ use clap:: ValueEnum ;
89use clap:: builder:: styling:: { AnsiColor , Effects , Style } ;
10+ use clap:: builder:: { PossibleValue , Styles , TypedValueParser , ValueParserFactory } ;
11+ use clap:: error:: ErrorKind ;
912use clap:: { Args , Parser , Subcommand } ;
1013
1114use uv_auth:: Service ;
@@ -587,8 +590,8 @@ pub struct VersionArgs {
587590 /// Update the project version using the given semantics
588591 ///
589592 /// This flag can be passed multiple times.
590- #[ arg( group = "operation" , long) ]
591- pub bump : Vec < VersionBump > ,
593+ #[ arg( group = "operation" , long, value_name = "BUMP[=VALUE]" ) ]
594+ pub bump : Vec < VersionBumpSpec > ,
592595
593596 /// Don't write a new version to the `pyproject.toml`
594597 ///
@@ -698,8 +701,8 @@ pub enum VersionBump {
698701 Dev ,
699702}
700703
701- impl std :: fmt :: Display for VersionBump {
702- fn fmt ( & self , f : & mut std :: fmt :: Formatter < ' _ > ) -> std :: fmt:: Result {
704+ impl Display for VersionBump {
705+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
703706 let string = match self {
704707 Self :: Major => "major" ,
705708 Self :: Minor => "minor" ,
@@ -715,6 +718,110 @@ impl std::fmt::Display for VersionBump {
715718 }
716719}
717720
721+ impl FromStr for VersionBump {
722+ type Err = String ;
723+
724+ fn from_str ( value : & str ) -> Result < Self , Self :: Err > {
725+ match value {
726+ "major" => Ok ( Self :: Major ) ,
727+ "minor" => Ok ( Self :: Minor ) ,
728+ "patch" => Ok ( Self :: Patch ) ,
729+ "stable" => Ok ( Self :: Stable ) ,
730+ "alpha" => Ok ( Self :: Alpha ) ,
731+ "beta" => Ok ( Self :: Beta ) ,
732+ "rc" => Ok ( Self :: Rc ) ,
733+ "post" => Ok ( Self :: Post ) ,
734+ "dev" => Ok ( Self :: Dev ) ,
735+ _ => Err ( format ! ( "invalid bump component `{value}`" ) ) ,
736+ }
737+ }
738+ }
739+
740+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
741+ pub struct VersionBumpSpec {
742+ pub bump : VersionBump ,
743+ pub value : Option < u64 > ,
744+ }
745+
746+ impl Display for VersionBumpSpec {
747+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
748+ match self . value {
749+ Some ( value) => write ! ( f, "{}={value}" , self . bump) ,
750+ None => self . bump . fmt ( f) ,
751+ }
752+ }
753+ }
754+
755+ impl FromStr for VersionBumpSpec {
756+ type Err = String ;
757+
758+ fn from_str ( input : & str ) -> Result < Self , Self :: Err > {
759+ let ( name, value) = match input. split_once ( '=' ) {
760+ Some ( ( name, value) ) => ( name, Some ( value) ) ,
761+ None => ( input, None ) ,
762+ } ;
763+
764+ let bump = name. parse :: < VersionBump > ( ) ?;
765+
766+ if bump == VersionBump :: Stable && value. is_some ( ) {
767+ return Err ( "`--bump stable` does not accept a value" . to_string ( ) ) ;
768+ }
769+
770+ let value = match value {
771+ Some ( "" ) => {
772+ return Err ( "`--bump` values cannot be empty" . to_string ( ) ) ;
773+ }
774+ Some ( raw) => Some (
775+ raw. parse :: < u64 > ( )
776+ . map_err ( |_| format ! ( "invalid numeric value `{raw}` for `--bump {name}`" ) ) ?,
777+ ) ,
778+ None => None ,
779+ } ;
780+
781+ Ok ( Self { bump, value } )
782+ }
783+ }
784+
785+ impl ValueParserFactory for VersionBumpSpec {
786+ type Parser = VersionBumpSpecValueParser ;
787+
788+ fn value_parser ( ) -> Self :: Parser {
789+ VersionBumpSpecValueParser
790+ }
791+ }
792+
793+ #[ derive( Clone , Debug ) ]
794+ pub struct VersionBumpSpecValueParser ;
795+
796+ impl TypedValueParser for VersionBumpSpecValueParser {
797+ type Value = VersionBumpSpec ;
798+
799+ fn parse_ref (
800+ & self ,
801+ _cmd : & clap:: Command ,
802+ _arg : Option < & clap:: Arg > ,
803+ value : & std:: ffi:: OsStr ,
804+ ) -> Result < Self :: Value , clap:: Error > {
805+ let raw = value. to_str ( ) . ok_or_else ( || {
806+ clap:: Error :: raw (
807+ ErrorKind :: InvalidUtf8 ,
808+ "`--bump` values must be valid UTF-8" ,
809+ )
810+ } ) ?;
811+
812+ VersionBumpSpec :: from_str ( raw)
813+ . map_err ( |message| clap:: Error :: raw ( ErrorKind :: InvalidValue , message) )
814+ }
815+
816+ fn possible_values ( & self ) -> Option < Box < dyn Iterator < Item = PossibleValue > + ' _ > > {
817+ Some ( Box :: new (
818+ VersionBump :: value_variants ( )
819+ . iter ( )
820+ . filter_map ( ValueEnum :: to_possible_value) ,
821+ ) )
822+ }
823+ }
824+
718825#[ derive( Args ) ]
719826pub struct SelfNamespace {
720827 #[ command( subcommand) ]
0 commit comments