@@ -7,7 +7,6 @@ use std::{
77 sync:: LazyLock ,
88} ;
99
10- use anyhow:: Context ;
1110use cmd_util:: env:: env_config;
1211use errors:: ErrorMetadata ;
1312pub use metrics:: {
@@ -73,7 +72,38 @@ impl ClientVersionState {
7372#[ derive( PartialEq , Eq , Clone , Ord , PartialOrd ) ]
7473pub struct ClientVersion {
7574 client : ClientType ,
76- version : Version ,
75+ version : ClientVersionIdent ,
76+ }
77+
78+ #[ derive( PartialEq , Eq , Clone , Ord , PartialOrd ) ]
79+ pub enum ClientVersionIdent {
80+ Semver ( Version ) ,
81+ Unrecognized ( String ) ,
82+ }
83+
84+ impl Display for ClientVersionIdent {
85+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
86+ match self {
87+ Self :: Semver ( v) => write ! ( f, "{v}" ) ,
88+ Self :: Unrecognized ( ident) => write ! ( f, "{ident}" ) ,
89+ }
90+ }
91+ }
92+
93+ impl ClientVersionIdent {
94+ fn below_threshold ( & self , threshold : & Version ) -> bool {
95+ match self {
96+ Self :: Semver ( version) => version <= threshold,
97+ Self :: Unrecognized ( _) => true ,
98+ }
99+ }
100+
101+ fn above_threshold ( & self , threshold : & Version ) -> bool {
102+ match self {
103+ Self :: Semver ( version) => version >= threshold,
104+ Self :: Unrecognized ( _) => true ,
105+ }
106+ }
77107}
78108
79109#[ derive( Clone , Debug , PartialEq , Eq , Ord , PartialOrd ) ]
@@ -95,7 +125,7 @@ pub enum ClientType {
95125}
96126
97127impl FromStr for ClientType {
98- type Err = anyhow :: Error ;
128+ type Err = ! ;
99129
100130 fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
101131 let client_type = match & * s. to_ascii_lowercase ( ) {
@@ -189,25 +219,38 @@ impl FromStr for ClientVersion {
189219 type Err = anyhow:: Error ;
190220
191221 fn from_str ( s : & str ) -> anyhow:: Result < Self > {
192- let em = ErrorMetadata :: bad_request (
193- "InvalidVersion" ,
194- format ! (
195- "Failed to parse client version string: '{s}'. Expected format is \
196- {{client_name}}-{{semver}}, e.g. my-esolang-client-0.0.1"
197- ) ,
198- ) ;
199-
200222 // Use the longest parseable semver spec from the right.
201223 let parts: Vec < & str > = s. split ( '-' ) . collect ( ) ;
202- let ( n, version) = ( 1 ..parts. len ( ) )
203- . find_map ( |n| {
204- Version :: parse ( parts[ n..parts. len ( ) ] . join ( "-" ) . as_str ( ) )
205- . map ( |v| ( n, v) )
206- . ok ( )
207- } )
208- . context ( em. clone ( ) ) ?;
209- let client_str = parts[ 0 ..n] . join ( "-" ) ;
210- let client = client_str. parse :: < ClientType > ( ) . context ( em) ?;
224+
225+ if parts. len ( ) < 2 {
226+ anyhow:: bail!( ErrorMetadata :: bad_request(
227+ "InvalidVersion" ,
228+ format!(
229+ "Failed to parse client version string: '{s}'. Expected format is \
230+ {{client_name}}-{{semver}}, e.g. my-esolang-client-0.0.1"
231+ ) ,
232+ ) ) ;
233+ }
234+
235+ let split = ( 1 ..parts. len ( ) ) . find_map ( |n| {
236+ Version :: parse ( parts[ n..parts. len ( ) ] . join ( "-" ) . as_str ( ) )
237+ . map ( |v| ( n, v) )
238+ . ok ( )
239+ } ) ;
240+ let ( client_str, version) = match split {
241+ Some ( ( n, version) ) => {
242+ let client_str = parts[ 0 ..n] . join ( "-" ) ;
243+ ( client_str, ClientVersionIdent :: Semver ( version) )
244+ } ,
245+ None => {
246+ let client_str = parts[ 0 ] . to_string ( ) ;
247+ let version = ClientVersionIdent :: Unrecognized ( parts[ 1 ..] . join ( "-" ) ) ;
248+ ( client_str, version)
249+ } ,
250+ } ;
251+ let Ok ( client) = client_str. parse :: < ClientType > ( ) else {
252+ unreachable ! ( )
253+ } ;
211254 Ok ( Self { client, version } )
212255 }
213256}
@@ -217,7 +260,7 @@ impl ClientVersion {
217260 & self . client
218261 }
219262
220- pub fn version ( & self ) -> & Version {
263+ pub fn version ( & self ) -> & ClientVersionIdent {
221264 & self . version
222265 }
223266
@@ -227,15 +270,15 @@ impl ClientVersion {
227270 let upgrade_instructions = self . client . upgrade_instructions ( ) ;
228271
229272 if let Some ( unsupported_threshold) = self . client . unsupported_threshold ( )
230- && self . version <= unsupported_threshold
273+ && self . version . below_threshold ( & unsupported_threshold)
231274 {
232275 return ClientVersionState :: Unsupported ( format ! (
233276 "The Convex {client} package at version {version} is no longer supported. \
234277 {upgrade_instructions}"
235278 ) ) ;
236279 }
237280 if let Some ( upgrade_required_threshold) = self . client . upgrade_required_threshold ( )
238- && self . version <= upgrade_required_threshold
281+ && self . version . below_threshold ( & upgrade_required_threshold)
239282 {
240283 return ClientVersionState :: UpgradeRequired ( format ! (
241284 "The Convex {client} package at {version} is deprecated and will no longer be \
@@ -257,14 +300,14 @@ impl ClientVersion {
257300 } else {
258301 ClientType :: CLI
259302 } ,
260- version : v ,
303+ version : ClientVersionIdent :: Semver ( v ) ,
261304 }
262305 }
263306
264307 pub fn unknown ( ) -> ClientVersion {
265308 Self {
266309 client : ClientType :: Unrecognized ( "unknown" . into ( ) ) ,
267- version : Version :: new ( 0 , 0 , 0 ) ,
310+ version : ClientVersionIdent :: Unrecognized ( "unknownversion" . into ( ) ) ,
268311 }
269312 }
270313
@@ -274,9 +317,9 @@ impl ClientVersion {
274317 pub fn should_require_format_param ( & self ) -> bool {
275318 match self . client ( ) {
276319 ClientType :: CLI | ClientType :: NPM | ClientType :: Actions => {
277- self . version ( ) >= & Version :: new ( 1 , 4 , 1 )
320+ self . version ( ) . above_threshold ( & Version :: new ( 1 , 4 , 1 ) )
278321 } ,
279- ClientType :: Python => self . version ( ) >= & Version :: new ( 0 , 5 , 0 ) ,
322+ ClientType :: Python => self . version ( ) . above_threshold ( & Version :: new ( 0 , 5 , 0 ) ) ,
280323 ClientType :: Rust
281324 | ClientType :: StreamingImport
282325 | ClientType :: AirbyteExport
@@ -316,6 +359,7 @@ mod tests {
316359 } ;
317360 use crate :: version:: {
318361 ClientType ,
362+ ClientVersionIdent ,
319363 DeprecationThreshold ,
320364 DEPRECATION_THRESHOLD ,
321365 } ;
@@ -341,12 +385,23 @@ mod tests {
341385 ) ;
342386 let client_version = ClientVersion {
343387 client : ClientType :: NPM ,
344- version : upgrade_required_version_plus_one,
388+ version : ClientVersionIdent :: Semver ( upgrade_required_version_plus_one) ,
345389 } ;
346390 assert_eq ! (
347391 client_version. current_state( ) ,
348392 ClientVersionState :: Supported
349393 ) ;
394+
395+ // Unknown version of NPM are unsupported
396+ let client_version = ClientVersion {
397+ client : ClientType :: NPM ,
398+ version : ClientVersionIdent :: Unrecognized ( "asdfdsasdf" . to_string ( ) ) ,
399+ } ;
400+ assert_matches ! (
401+ client_version. current_state( ) ,
402+ ClientVersionState :: Unsupported ( _)
403+ ) ;
404+
350405 // Versions higher than what we know about are also considered latest.
351406 assert_eq ! (
352407 "python-convex-1000.0.0"
@@ -358,10 +413,16 @@ mod tests {
358413 "streaming-import-0.0.10" . parse:: <ClientVersion >( ) ?,
359414 ClientVersion {
360415 client: ClientType :: StreamingImport ,
361- version: Version :: new( 0 , 0 , 10 )
416+ version: ClientVersionIdent :: Semver ( Version :: new( 0 , 0 , 10 ) )
362417 }
363418 ) ;
364- assert ! ( "npm-1.2.3.4" . parse:: <ClientVersion >( ) . is_err( ) ) ;
419+
420+ // Not a valid semver
421+ assert_matches ! (
422+ "npm-1.2.3.4" . parse:: <ClientVersion >( ) ?. current_state( ) ,
423+ ClientVersionState :: Unsupported ( _)
424+ ) ;
425+
365426 assert_eq ! (
366427 & format!( "{}" , "npm-0.0.10" . parse:: <ClientVersion >( ) ?) ,
367428 "npm-0.0.10"
@@ -386,15 +447,21 @@ mod tests {
386447 "some-custom-thing-1.2.3-4.5.6-alpha.7" . parse:: <ClientVersion >( ) ?,
387448 ClientVersion {
388449 client: ClientType :: Unrecognized ( "some-custom-thing" . to_string( ) ) ,
389- version: Version {
450+ version: ClientVersionIdent :: Semver ( Version {
390451 major: 1 ,
391452 minor: 2 ,
392453 patch: 3 ,
393454 pre: Prerelease :: new( "4.5.6-alpha.7" ) ?,
394455 build: BuildMetadata :: EMPTY
395- }
456+ } )
396457 }
397458 ) ;
459+ assert_eq ! (
460+ "big_brain-20240412T160958Z-baea64010a12"
461+ . parse:: <ClientVersion >( ) ?
462+ . current_state( ) ,
463+ ClientVersionState :: Supported
464+ ) ;
398465 Ok ( ( ) )
399466 }
400467
0 commit comments