Skip to content

Commit b1de652

Browse files
committed
simplified Formatting to support linux
1 parent 0342495 commit b1de652

5 files changed

Lines changed: 107 additions & 112 deletions

File tree

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,10 @@ Example Use:
237237
```
238238
let measure = 28.123.measured(in: .meter)
239239
240-
measure.formatted() // -> "28.123 m"
241-
measure.formatted(precision: .significantDigits(1) // -> "30 m"
242-
measure.formatted(precision: .significantDigits(3)) // -> "28.1 m"
243-
measure.formatted(precision:
244-
.integerAndFractionLength(integer: 2, fraction: 0)) // -> "28 m"
240+
measure.formatted() // -> "28.123 m"
241+
measure.formatted(minimumFractionDigits: 4) // -> "28.1230 m"
242+
measure.formatted(maximumFractionDigits: 0) // -> "28 m"
243+
measure.formatted(maximumFractionDigits: 1) // -> "28.1 m"
245244
```
246245

247246
## CLI
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//
2+
// Formatter.swift
3+
// Units
4+
// (aka Fountation.FormatStyle)
5+
//
6+
// Created by Jason Jobe on 10/24/25.
7+
//
8+
9+
public extension Measurement {
10+
struct Formatter<Output> {
11+
let format: (Measurement) -> Output
12+
}
13+
14+
func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
15+
formatter.format(self)
16+
}
17+
18+
func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
19+
formatter.format(self)
20+
}
21+
22+
func formatted(
23+
minimumFractionDigits: Int = 0,
24+
maximumFractionDigits: Int = 4
25+
) -> String {
26+
Formatter
27+
.measurement(
28+
minimumFractionDigits: minimumFractionDigits,
29+
maximumFractionDigits: maximumFractionDigits)
30+
.format(self)
31+
}
32+
}
33+
34+
extension Measurement.Formatter where Output == String {
35+
public static func measurement(
36+
minimumFractionDigits: Int = 0,
37+
maximumFractionDigits: Int = 4
38+
) -> Self {
39+
self.init { value in
40+
value.value
41+
.formatted(
42+
minimumFractionDigits: minimumFractionDigits,
43+
maximumFractionDigits: maximumFractionDigits)
44+
+ " \(value.unit.symbol)"
45+
}
46+
}
47+
}
48+
49+
50+
// MARK: Percent Formatter
51+
52+
public extension Percent {
53+
struct Formatter<Output> {
54+
let format: (Percent) -> Output
55+
}
56+
57+
func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
58+
formatter.format(self)
59+
}
60+
}
61+
62+
public extension Percent {
63+
func formatted(fractionDigits: Int = 2) -> String {
64+
Formatter(fractionDigits: fractionDigits).format(self)
65+
}
66+
}
67+
68+
public extension Percent.Formatter where Output == String {
69+
70+
init (fractionDigits: Int) {
71+
self.init { value in
72+
(value.magnitude * 100)
73+
.formatted(
74+
minimumFractionDigits: 0,
75+
maximumFractionDigits: fractionDigits)
76+
+ value.unit.symbol
77+
}
78+
}
79+
}
80+
81+
public extension BinaryFloatingPoint {
82+
func formatted(minimumFractionDigits: Int = 0, maximumFractionDigits: Int = 4) -> String {
83+
let minDigits = max(0, minimumFractionDigits)
84+
let maxDigits = max(minDigits, maximumFractionDigits)
85+
let s = String(format: "%.\(maxDigits)f", Double(self))
86+
if maxDigits > minDigits, s.contains(".") {
87+
var trimmed = s
88+
while trimmed.last == "0" { trimmed.removeLast() }
89+
if trimmed.last == "." { trimmed.removeLast() }
90+
if let dotIndex = trimmed.firstIndex(of: ".") {
91+
let fractionalCount = trimmed.distance(from: trimmed.index(after: dotIndex), to: trimmed.endIndex)
92+
if fractionalCount < minDigits {
93+
return String(format: "%.\(minDigits)f", Double(self))
94+
}
95+
}
96+
return trimmed
97+
}
98+
return s
99+
}
100+
}

Sources/Units/Measurement/Measurement.swift

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -194,58 +194,3 @@ extension Measurement: ExpressibleByFloatLiteral {
194194
}
195195

196196
extension Measurement: Sendable {}
197-
198-
// MARK: FormatStyle
199-
@available(macOS 12.0, iOS 15.0, *)
200-
public extension Measurement {
201-
202-
func formatted<Style: FormatStyle>(
203-
_ style: Style
204-
) -> Style.FormatOutput where Style.FormatInput == Self {
205-
style.format(self)
206-
}
207-
}
208-
209-
@available(macOS 12.0, iOS 15.0, *)
210-
public extension Measurement {
211-
typealias Precision = NumberFormatStyleConfiguration.Precision
212-
213-
struct Formatter<Output> {
214-
let format: (Measurement) -> Output
215-
}
216-
217-
func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
218-
formatter.format(self)
219-
}
220-
221-
func formatted(_ formatter: Formatter<String> = .measurement()) -> String {
222-
formatter.format(self)
223-
}
224-
225-
@_disfavoredOverload
226-
func formatted(precision: Precision? = nil) -> String {
227-
formatted(.measurement(precision: precision))
228-
}
229-
}
230-
231-
@available(macOS 12.0, iOS 15.0, *)
232-
extension Measurement.Formatter where Output == String {
233-
public typealias Precision = NumberFormatStyleConfiguration.Precision
234-
235-
public static func measurement(precision: Precision? = nil) -> Self {
236-
.init { value in
237-
switch (value.unit, precision) {
238-
case (.none, .none):
239-
"\(value.value.formatted(.number))"
240-
case (.none, .some(let p)):
241-
"\(value.value.formatted(.number.precision(p)))"
242-
case (let u, .none):
243-
"\(value.value.formatted(.number)) \(u)"
244-
245-
case (let u, .some(let p)):
246-
"\(value.value.formatted(.number.precision(p))) \(u)"
247-
}
248-
}
249-
}
250-
}
251-

Sources/Units/Measurement/Percent+Measurement.swift

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -229,52 +229,3 @@ extension Percent {
229229
self.init(magnitude: .random(in: range.lowerBound.magnitude...range.upperBound.magnitude))
230230
}
231231
}
232-
233-
// MARK: FormatStyle
234-
@available(macOS 12.0, iOS 15.0, *)
235-
public extension Percent {
236-
237-
func formatted<Style: FormatStyle>(
238-
_ style: Style
239-
) -> Style.FormatOutput where Style.FormatInput == Self {
240-
style.format(self)
241-
}
242-
}
243-
244-
public extension Percent {
245-
struct Formatter<Output> {
246-
let format: (Percent) -> Output
247-
}
248-
249-
func formatted<Output>(_ formatter: Formatter<Output>) -> Output {
250-
formatter.format(self)
251-
}
252-
}
253-
254-
@available(macOS 12.0, iOS 15.0, *)
255-
public extension Percent {
256-
func formatted(_ formatter: Formatter<String> = .percent) -> String {
257-
formatter.format(self)
258-
}
259-
260-
func formatted(fractionDigits: Int) -> String {
261-
Formatter(fractionDigits: fractionDigits).format(self)
262-
}
263-
}
264-
265-
@available(macOS 12.0, iOS 15.0, *)
266-
public extension Percent.Formatter where Output == String {
267-
268-
init (fractionDigits: Int) {
269-
self.init { value in
270-
value.magnitude
271-
.formatted(.percent.precision(.fractionLength(fractionDigits)))
272-
}
273-
}
274-
275-
static var percent: Self {
276-
.init { value in
277-
return value.magnitude.formatted(.percent)
278-
}
279-
}
280-
}

Tests/UnitsTests/MeasurementTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,8 +604,8 @@ final class MeasurementTests: XCTestCase {
604604
func testFormatStyle() {
605605
let measure = 28.123.measured(in: .meter)
606606
XCTAssertEqual(measure.formatted(), "28.123 m")
607-
XCTAssertEqual(measure.formatted(precision: .significantDigits(1)), "30 m")
608-
XCTAssertEqual(measure.formatted(precision: .significantDigits(3)), "28.1 m")
609-
XCTAssertEqual(measure.formatted(precision: .integerAndFractionLength(integer: 2, fraction: 0)), "28 m")
607+
XCTAssertEqual(measure.formatted(minimumFractionDigits: 4), "28.1230 m")
608+
XCTAssertEqual(measure.formatted(maximumFractionDigits: 0), "28 m")
609+
XCTAssertEqual(measure.formatted(maximumFractionDigits: 1), "28.1 m")
610610
}
611611
}

0 commit comments

Comments
 (0)