# frozen_string_literal: true# Parse an amount from a stringclassMoneyParserclassMoneyFormatError<ArgumentError;endMARKS=%w[. , · ’ ˙ ']+[' ']ESCAPED_MARKS=Regexp.escape(MARKS.join)ESCAPED_NON_SPACE_MARKS=Regexp.escape((MARKS-[' ']).join)ESCAPED_NON_DOT_MARKS=Regexp.escape((MARKS-['.']).join)ESCAPED_NON_COMMA_MARKS=Regexp.escape((MARKS-[',']).join)NUMERIC_REGEX=/(
[\+\-]?
[\d#{ESCAPED_NON_SPACE_MARKS}][\d#{ESCAPED_MARKS}]*
)/ix# 1,234,567.89DOT_DECIMAL_REGEX=/\A
[\+\-]?
(?:
(?:\d+)
(?:[#{ESCAPED_NON_DOT_MARKS}]\d{3})+
(?:\.\d{2,})?
)
\z/ix# 1.234.567,89COMMA_DECIMAL_REGEX=/\A
[\+\-]?
(?:
(?:\d+)
(?:[#{ESCAPED_NON_COMMA_MARKS}]\d{3})+
(?:\,\d{2,})?
)
\z/ix# 12,34,567.89INDIAN_NUMERIC_REGEX=/\A
[\+\-]?
(?:
(?:\d+)
(?:\,\d{2})+
(?:\,\d{3})
(?:\.\d{2})?
)
\z/ix# 1,1123,4567.89CHINESE_NUMERIC_REGEX=/\A
[\+\-]?
(?:
(?:\d+)
(?:\,\d{4})+
(?:\.\d{2})?
)
\z/ixdefself.parse(input,currency=nil,**options)new.parse(input,currency,**options)enddefparse(input,currency=nil,strict: false)currency=Money::Helpers.value_to_currency(currency)amount=extract_amount_from_string(input,currency,strict)Money.new(amount,currency)endprivatedefextract_amount_from_string(input,currency,strict)unlessinput.is_a?(String)returninputendifinput.strip.empty?return'0'endnumber=input.scan(NUMERIC_REGEX).flatten.firstnumber=number.to_s.stripifnumber.empty?raiseMoneyFormatError,"invalid money string: #{input}"ifstrictMoney.deprecate("invalid money strings will raise in the next major release \"#{input}\"")return'0'endmarks=number.scan(/[#{ESCAPED_MARKS}]/).flattenifmarks.empty?returnnumberendifmarks.size==1returnnormalize_number(number,marks,currency)end# remove end of string marknumber.sub!(/[#{ESCAPED_MARKS}]\z/,'')ifamount=number[DOT_DECIMAL_REGEX]||number[INDIAN_NUMERIC_REGEX]||number[CHINESE_NUMERIC_REGEX]returnamount.tr(ESCAPED_NON_DOT_MARKS,'')endifamount=number[COMMA_DECIMAL_REGEX]returnamount.tr(ESCAPED_NON_COMMA_MARKS,'').sub(',','.')endraiseMoneyFormatError,"invalid money string: #{input}"ifstrictMoney.deprecate("invalid money strings will raise in the next major release \"#{input}\"")normalize_number(number,marks,currency)enddefnormalize_number(number,marks,currency)digits=number.rpartition(marks.last)digits.first.tr!(ESCAPED_MARKS,'')iflast_digits_decimals?(digits,marks,currency)"#{digits.first}.#{digits.last}"else"#{digits.first}#{digits.last}"endenddeflast_digits_decimals?(digits,marks,currency)# Thousands marks are always different from decimal marks# Example: 1,234,456*other_marks,last_mark=marksother_marks.uniq!ifother_marks.size==1returnother_marks.first!=last_markend# Thousands always have more than 2 digits# Example: 1,23 must be 1 dollar and 23 centsifdigits.last.size<3return!digits.last.empty?end# 0 before the final mark indicates last digits are decimals# Example: 0,23ifdigits.first.to_i.zero?returntrueend# legacy support for 1.000 USDifdigits.last.size==3&&digits.first.size<=3&¤cy.minor_units<3returnfalseend# The last mark matches the one used by the provided currency to delimiter decimalscurrency.decimal_mark==last_markendend