feat: accept unambiguous currency names
This commit is contained in:
@@ -103,9 +103,28 @@ public enum Calculator {
|
||||
"wbtc", "weth",
|
||||
]
|
||||
|
||||
private static let currencyNames: [String: String] = [
|
||||
"yen": "jpy", "japanese yen": "jpy",
|
||||
"euro": "eur", "euros": "eur",
|
||||
"danish krone": "dkk", "danish kroner": "dkk",
|
||||
"swedish krona": "sek", "swedish kronor": "sek",
|
||||
"norwegian krone": "nok", "norwegian kroner": "nok",
|
||||
"us dollar": "usd", "us dollars": "usd",
|
||||
"american dollar": "usd", "american dollars": "usd",
|
||||
"canadian dollar": "cad", "canadian dollars": "cad",
|
||||
"australian dollar": "aud", "australian dollars": "aud",
|
||||
"new zealand dollar": "nzd", "new zealand dollars": "nzd",
|
||||
"british pound": "gbp", "british pounds": "gbp", "pound sterling": "gbp",
|
||||
"swiss franc": "chf", "swiss francs": "chf",
|
||||
"chinese yuan": "cny", "yuan": "cny",
|
||||
"indian rupee": "inr", "indian rupees": "inr",
|
||||
"south korean won": "krw",
|
||||
"bitcoin": "btc", "bitcoins": "btc", "ether": "eth", "ethereum": "eth",
|
||||
]
|
||||
|
||||
private static func currencyCode(_ text: String, rates: CurrencyRates?) -> String? {
|
||||
let code = text.lowercased()
|
||||
if code == "yen" { return "jpy" }
|
||||
if let code = currencyNames[code] { return code }
|
||||
guard (2...12).contains(code.utf8.count),
|
||||
code.utf8.allSatisfy({ (97...122).contains($0) || (48...57).contains($0) }),
|
||||
ConversionUnit.parse(text) == nil else { return nil }
|
||||
@@ -116,21 +135,25 @@ public enum Calculator {
|
||||
private static func currencyQuery(_ query: String, rates: CurrencyRates?) -> CurrencyQuery? {
|
||||
guard query.utf8.count <= 512 else { return nil }
|
||||
let words = query.split(whereSeparator: \.isWhitespace)
|
||||
guard words.count >= 3,
|
||||
["to", "in", "as"].contains(words[words.count - 2].lowercased()),
|
||||
let target = currencyCode(String(words[words.count - 1]), rates: rates) else { return nil }
|
||||
let sourceText = words.dropLast(2).joined(separator: " ")
|
||||
let characters = Array(sourceText)
|
||||
guard characters.count > 1 else { return nil }
|
||||
for length in stride(from: min(12, characters.count - 1), through: 2, by: -1) {
|
||||
let suffix = String(characters.suffix(length))
|
||||
guard let source = currencyCode(suffix, rates: rates) else { continue }
|
||||
let boundary = characters[characters.count - length - 1]
|
||||
// Digit-bearing tickers need a space so 21inch cannot silently become 2 × 1INCH.
|
||||
guard boundary.isWhitespace || (suffix.allSatisfy(\.isLetter) && !boundary.isLetter) else { continue }
|
||||
let expression = String(characters.dropLast(length)).trimmingCharacters(in: .whitespaces)
|
||||
guard let amount = parse(expression) else { continue }
|
||||
return CurrencyQuery(expression: expression, source: source, target: target, amount: amount)
|
||||
guard words.count >= 3 else { return nil }
|
||||
for separator in 1..<(words.count - 1)
|
||||
where ["to", "in", "as"].contains(words[separator].lowercased()) {
|
||||
guard let target = currencyCode(words[(separator + 1)...].joined(separator: " "), rates: rates) else {
|
||||
continue
|
||||
}
|
||||
let sourceText = words[..<separator].joined(separator: " ")
|
||||
let characters = Array(sourceText)
|
||||
guard characters.count > 1 else { continue }
|
||||
for length in stride(from: min(32, characters.count - 1), through: 2, by: -1) {
|
||||
let suffix = String(characters.suffix(length))
|
||||
guard let source = currencyCode(suffix, rates: rates) else { continue }
|
||||
let boundary = characters[characters.count - length - 1]
|
||||
// Digit-bearing tickers need a space so 21inch cannot silently become 2 × 1INCH.
|
||||
guard boundary.isWhitespace || (suffix.allSatisfy { $0.isLetter || $0.isWhitespace } && !boundary.isLetter) else { continue }
|
||||
let expression = String(characters.dropLast(length)).trimmingCharacters(in: .whitespaces)
|
||||
guard let amount = parse(expression) else { continue }
|
||||
return CurrencyQuery(expression: expression, source: source, target: target, amount: amount)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user