@@ -396,67 +396,92 @@ defmodule Version do
396396 Enum . filter ( Enum . reverse ( acc ) , & ( & 1 != :' ' ) )
397397 end
398398
399- @ version_regex ~r/ ^
400- (\d +) # major
401- (?:\. (\d +))? # minor
402- (?:\. (\d +))? # patch
403- (?:\- ([\d \w \. \- ]+))? # pre
404- (?:\+ ([\d \w \. \- ]+))? # build
405- $/ x
406-
407399 @ spec parse_requirement ( String . t ) :: { :ok , term } | :error
408400 def parse_requirement ( source ) do
409401 lexed = lexer ( source , [ ] )
410402 to_matchspec ( lexed )
411403 end
412404
413- defp nillify ( "" ) , do: nil
414- defp nillify ( o ) , do: o
415-
416405 @ spec parse_version ( String . t ) :: { :ok , Version . matchable } | :error
417406 def parse_version ( string , approximate? \\ false ) when is_binary ( string ) do
418- if parsed = Regex . run ( @ version_regex , string ) do
419- destructure [ _ , major , minor , patch , pre ] , parsed
420- patch = nillify ( patch )
421- pre = nillify ( pre )
422-
423- if is_nil ( minor ) or ( is_nil ( patch ) and not approximate? ) do
424- :error
425- else
426- major = String . to_integer ( major )
427- minor = String . to_integer ( minor )
428- patch = patch && String . to_integer ( patch )
429-
430- case parse_pre ( pre ) do
431- { :ok , pre } ->
432- { :ok , { major , minor , patch , pre } }
433- :error ->
434- :error
435- end
436- end
407+ destructure [ version_with_pre , build ] , String . split ( string , "+" , parts: 2 )
408+ destructure [ version , pre ] , String . split ( version_with_pre , "-" , parts: 2 )
409+ destructure [ major , minor , patch ] , String . split ( version , "." )
410+
411+ with { :ok , major } <- require_digits ( major ) ,
412+ { :ok , minor } <- require_digits ( minor ) ,
413+ { :ok , patch } <- maybe_patch ( patch , approximate? ) ,
414+ { :ok , pre_parts } <- optional_dot_separated ( pre ) ,
415+ { :ok , pre_parts } <- convert_parts_to_integer ( pre_parts , [ ] ) ,
416+ { :ok , _build_parts } <- optional_dot_separated ( build ) do
417+ { :ok , { major , minor , patch , pre_parts } }
437418 else
438- :error
419+ _other -> :error
439420 end
440421 end
441422
442- defp parse_pre ( nil ) , do: { :ok , [ ] }
443- defp parse_pre ( pre ) , do: parse_pre ( String . split ( pre , "." ) , [ ] )
423+ defp require_digits ( nil ) , do: :error
424+ defp require_digits ( string ) do
425+ if leading_zero? ( string ) , do: :error , else: parse_digits ( string , "" )
426+ end
444427
445- defp parse_pre ( [ piece | t ] , acc ) do
446- cond do
447- piece =~ ~r/ ^(0|[1-9][0-9]*)$/ ->
448- parse_pre ( t , [ String . to_integer ( piece ) | acc ] )
449- piece =~ ~r/ ^[0-9]*$/ ->
450- :error
451- true ->
452- parse_pre ( t , [ piece | acc ] )
428+ defp leading_zero? ( << ?0 , _ , _ :: binary >> ) , do: true
429+ defp leading_zero? ( _ ) , do: false
430+
431+ defp parse_digits ( << char , rest :: binary >> , acc ) when char in ?0 .. ?9 ,
432+ do: parse_digits ( rest , << acc :: binary , char >> )
433+ defp parse_digits ( << >> , acc ) when byte_size ( acc ) > 0 ,
434+ do: { :ok , String . to_integer ( acc ) }
435+ defp parse_digits ( _ , _acc ) ,
436+ do: :error
437+
438+ defp maybe_patch ( patch , approximate? )
439+ defp maybe_patch ( nil , true ) , do: { :ok , nil }
440+ defp maybe_patch ( patch , _ ) , do: require_digits ( patch )
441+
442+ defp optional_dot_separated ( nil ) , do: { :ok , [ ] }
443+ defp optional_dot_separated ( string ) do
444+ parts = String . split ( string , "." )
445+ if Enum . all? ( parts , & ( & 1 != "" and valid_identifier? ( & 1 ) ) ) do
446+ { :ok , parts }
447+ else
448+ :error
453449 end
454450 end
455451
456- defp parse_pre ( [ ] , acc ) do
452+ defp convert_parts_to_integer ( [ part | rest ] , acc ) do
453+ case parse_digits ( part , "" ) do
454+ { :ok , integer } ->
455+ if leading_zero? ( part ) do
456+ :error
457+ else
458+ convert_parts_to_integer ( rest , [ integer | acc ] )
459+ end
460+ :error ->
461+ convert_parts_to_integer ( rest , [ part | acc ] )
462+ end
463+ end
464+
465+ defp convert_parts_to_integer ( [ ] , acc ) do
457466 { :ok , Enum . reverse ( acc ) }
458467 end
459468
469+ defp valid_identifier? ( << char , rest :: binary >> )
470+ when char in ?0 .. ?9
471+ when char in ?a .. ?z
472+ when char in ?A .. ?Z
473+ when char == ?- do
474+ valid_identifier? ( rest )
475+ end
476+
477+ defp valid_identifier? ( << >> ) do
478+ true
479+ end
480+
481+ defp valid_identifier? ( _other ) do
482+ false
483+ end
484+
460485 defp valid_requirement? ( [ ] ) , do: false
461486 defp valid_requirement? ( [ a | next ] ) , do: valid_requirement? ( a , next )
462487
0 commit comments