TeraFoodDecimalEntry.swift (1891B)
1 import Foundation 2 3 /// Native entry translation only. Shared Rust constructors still validate the 4 /// canonical amount, digit budget, price/quantity rules, currency and unit. 5 enum TeraFoodDecimalEntry { 6 static func canonicalOrRaw(_ raw: String?, locale: Locale) -> String? { 7 guard let raw, !raw.isEmpty else { return raw } 8 let separator = locale.decimalSeparator ?? "." 9 // A canonical dot remains usable after a draft moves between locales. 10 guard separator == "." || !raw.contains(separator) || !raw.contains(".") else { return raw } 11 let text = raw.replacingOccurrences(of: separator, with: ".") 12 let parts = text.split(separator: ".", omittingEmptySubsequences: false) 13 guard parts.count <= 2, let first = parts.first, !first.isEmpty, 14 parts.allSatisfy({ !$0.isEmpty }) else { return raw } 15 var digits: [String] = [] 16 for part in parts { 17 var translated = "" 18 for character in part { 19 guard character.unicodeScalars.count == 1, 20 character.unicodeScalars.first?.properties.generalCategory == .decimalNumber, 21 let value = character.wholeNumberValue else { return raw } 22 translated += String(value) 23 } 24 digits.append(translated) 25 } 26 let integer = String(digits[0].drop(while: { $0 == "0" })) 27 let whole = integer.isEmpty ? "0" : integer 28 guard digits.count == 2 else { return whole } 29 let fraction = String(digits[1].reversed().drop(while: { $0 == "0" }).reversed()) 30 return fraction.isEmpty ? whole : whole + "." + fraction 31 } 32 33 static func form(_ input: TeraAddForm, locale: Locale) -> TeraAddForm { 34 guard input.commandType == .createFoodAvailability else { return input } 35 var result = input 36 result.priceAmount = canonicalOrRaw(input.priceAmount, locale: locale) 37 result.quantity = canonicalOrRaw(input.quantity, locale: locale) 38 return result 39 } 40 }