Files
swift-openfoodfacts-sdk/Sources/OpenFoodFactsSDK/Schemas/Nutriments.swift
cdricms 043de16a16 Api v2
2025-12-06 16:33:17 +01:00

53 lines
1.8 KiB
Swift

import FoundationEssentials
@dynamicMemberLookup
public struct Nutriments: Codable, Sendable {
private var storage: [String: Double]
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: AnyCodingKey.self)
var dict = [String: Double]()
for key in container.allKeys {
if let val = try? container.decode(Double.self, forKey: key) {
dict[key.stringValue] = val
} else if let valStr = try? container.decode(
String.self, forKey: key), let val = Double(valStr)
{
dict[key.stringValue] = val
}
}
self.storage = dict
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyCodingKey.self)
for (key, value) in storage {
try container.encode(value, forKey: AnyCodingKey(stringValue: key))
}
}
/// Access any nutrient dynamically (e.g. `nutriments.energyKcal`)
public subscript(dynamicMember member: String) -> Double? {
// Convert camelCase "energyKcal" to snake_case "energy-kcal" or "energy_kcal" logic if needed
// For V2, OFF often returns "energy-kcal_100g"
let snake = member.camelCaseToSnakeCase()
return storage[snake] ?? storage["\(snake)_100g"]
?? storage["\(snake)_value"]
}
// Specific standard getters
public var energyKcal: Double? { self.storage["energy-kcal_100g"] }
public var carbohydrates: Double? { self.storage["carbohydrates_100g"] }
public var fat: Double? { self.storage["fat_100g"] }
public var proteins: Double? { self.storage["proteins_100g"] }
public var salt: Double? { self.storage["salt_100g"] }
}
// Helper for String extension used above
struct AnyCodingKey: CodingKey {
var stringValue: String
var intValue: Int?
init(stringValue: String) { self.stringValue = stringValue }
init?(intValue: Int) { return nil }
}