field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

TeraExactDecimalPresentation.swift (2275B)


      1 import Foundation
      2 
      3 /// Display admitted wire decimals as strings. NumberFormatter supplies locale
      4 /// symbols and grouping only; it never receives the full monetary value.
      5 struct TeraExactDecimalPresentation {
      6   var locale: Locale = .current
      7 
      8   func string(_ canonical: String) -> String? {
      9     // The public food contract admits at most 28 digits plus one decimal dot.
     10     // This is a bounded presentation guard, not a second publication validator.
     11     guard canonical.utf8.count <= 29 else { return nil }
     12     let parts = canonical.split(separator: ".", omittingEmptySubsequences: false)
     13     guard (1 ... 2).contains(parts.count), parts.allSatisfy({ !$0.isEmpty }),
     14           parts.allSatisfy({ $0.utf8.allSatisfy { (48 ... 57).contains($0) } }),
     15           parts.reduce(0, { $0 + $1.utf8.count }) <= 28 else { return nil }
     16     let format = NumberFormatter()
     17     format.locale = locale
     18     format.numberStyle = .decimal
     19     let glyphs = (0 ... 9).map { format.string(from: NSNumber(value: $0)) ?? String($0) }
     20     let whole = grouped(parts[0], format: format)
     21     let localized = whole.utf8.map { byte -> String in
     22       (48 ... 57).contains(byte) ? glyphs[Int(byte - 48)] : String(UnicodeScalar(byte))
     23     }.joined()
     24     // Translate digits before introducing possibly multibyte grouping symbols.
     25     let integer = localized.replacingOccurrences(of: "|", with: format.groupingSeparator ?? "")
     26     guard parts.count == 2 else { return integer }
     27     let fraction = parts[1].utf8.map { glyphs[Int($0 - 48)] }.joined()
     28     return integer + (format.decimalSeparator ?? ".") + fraction
     29   }
     30 
     31   private func grouped(_ whole: Substring, format: NumberFormatter) -> String {
     32     let primary = format.groupingSize
     33     guard format.usesGroupingSeparator, primary > 0, whole.count > primary else { return String(whole) }
     34     let secondary = format.secondaryGroupingSize > 0 ? format.secondaryGroupingSize : primary
     35     var end = whole.endIndex
     36     var width = primary
     37     var groups: [Substring] = []
     38     while end > whole.startIndex {
     39       let start = whole.index(end, offsetBy: -width, limitedBy: whole.startIndex) ?? whole.startIndex
     40       groups.append(whole[start ..< end])
     41       end = start
     42       width = secondary
     43     }
     44     return groups.reversed().joined(separator: "|")
     45   }
     46 }