mirror of
https://github.com/apple/container.git
synced 2026-09-13 19:25:47 +00:00
Use string representation for CIDR and IPAddress types (#458)
This change adds extension to the CIDR and IPAddress types to implement custom encode/decode functions for `Codable` conformance to use their string representation as the output from encode and input to decode. This would make the output from encoding this type (e.g. JSON) more human-readable rather than using the internal integer representation.
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
|
||||
/// Describes an IPv4 CIDR address block.
|
||||
@frozen
|
||||
public struct CIDRv4: CustomStringConvertible, Equatable, Sendable, Hashable, Codable {
|
||||
public struct CIDRv4: CustomStringConvertible, Equatable, Sendable, Hashable {
|
||||
/// The IP component of this CIDR address.
|
||||
public let address: IPv4Address
|
||||
|
||||
@@ -99,3 +99,16 @@ public struct CIDRv4: CustomStringConvertible, Equatable, Sendable, Hashable, Co
|
||||
"\(address)/\(prefix)"
|
||||
}
|
||||
}
|
||||
|
||||
extension CIDRv4: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
let string = try container.decode(String.self)
|
||||
try self.init(string)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(description)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/// Describes an IPv4 or IPv6 CIDR address block.
|
||||
@frozen
|
||||
public struct CIDRv6: CustomStringConvertible, Equatable, Sendable, Hashable, Codable {
|
||||
public struct CIDRv6: CustomStringConvertible, Equatable, Sendable, Hashable {
|
||||
|
||||
/// The IP component of this CIDR address.
|
||||
public let address: IPv6Address
|
||||
@@ -100,3 +100,16 @@ public struct CIDRv6: CustomStringConvertible, Equatable, Sendable, Hashable, Co
|
||||
"\(address)/\(prefix)"
|
||||
}
|
||||
}
|
||||
|
||||
extension CIDRv6: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
let string = try container.decode(String.self)
|
||||
try self.init(string)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(description)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@frozen
|
||||
public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable, Codable {
|
||||
public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable {
|
||||
public let value: UInt32
|
||||
|
||||
@inlinable
|
||||
@@ -215,3 +215,16 @@ public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatabl
|
||||
lhs.value < rhs.value
|
||||
}
|
||||
}
|
||||
|
||||
extension IPv4Address: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
let string = try container.decode(String.self)
|
||||
try self.init(string)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(description)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Represents an IPv6 network address conforming to RFC 5952 and RFC 4291.
|
||||
public struct IPv6Address: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable, Codable {
|
||||
public struct IPv6Address: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable {
|
||||
public let value: UInt128
|
||||
|
||||
public let zone: String?
|
||||
@@ -252,3 +252,16 @@ public struct IPv6Address: Sendable, Hashable, CustomStringConvertible, Equatabl
|
||||
return (lhs.zone ?? "") < (rhs.zone ?? "")
|
||||
}
|
||||
}
|
||||
|
||||
extension IPv6Address: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
let string = try container.decode(String.self)
|
||||
try self.init(string)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(description)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@testable import ContainerizationExtras
|
||||
@@ -308,4 +309,66 @@ struct TestCIDR {
|
||||
let cidr = try CIDR("192.168.1.100/24")
|
||||
#expect(cidr.description == "192.168.1.100/24")
|
||||
}
|
||||
|
||||
@Test(
|
||||
"CIDRv4 Codable encodes to string representation",
|
||||
arguments: [
|
||||
"192.168.1.0/24",
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
]
|
||||
)
|
||||
func testCIDRv4CodableEncode(cidr: String) throws {
|
||||
let original = try CIDRv4(cidr)
|
||||
let encoded = try JSONEncoder().encode(original)
|
||||
let jsonString = String(data: encoded, encoding: .utf8)!
|
||||
#expect(jsonString.contains(original.address.description))
|
||||
#expect(jsonString.contains("\(original.prefix.length)"))
|
||||
}
|
||||
|
||||
@Test(
|
||||
"CIDRv4 Codable decodes from string representation",
|
||||
arguments: [
|
||||
"192.168.1.0/24",
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
]
|
||||
)
|
||||
func testCIDRv4CodableDecode(cidr: String) throws {
|
||||
let json = Data("\"\(cidr)\"".utf8)
|
||||
let decoded = try JSONDecoder().decode(CIDRv4.self, from: json)
|
||||
let expected = try CIDRv4(cidr)
|
||||
#expect(decoded == expected)
|
||||
}
|
||||
|
||||
@Test(
|
||||
"CIDRv6 Codable encodes to string representation",
|
||||
arguments: [
|
||||
("2001:db8::/32", "2001:db8::", 32),
|
||||
("fe80::/10", "fe80::", 10),
|
||||
("::1/128", "::1", 128),
|
||||
]
|
||||
)
|
||||
func testCIDRv6CodableEncode(cidr: String, expectedAddr: String, expectedPrefix: UInt8) throws {
|
||||
let original = try CIDRv6(cidr)
|
||||
let encoded = try JSONEncoder().encode(original)
|
||||
let jsonString = String(data: encoded, encoding: .utf8)!
|
||||
#expect(jsonString.contains(expectedAddr))
|
||||
#expect(jsonString.contains("\(expectedPrefix)"))
|
||||
}
|
||||
|
||||
@Test(
|
||||
"CIDRv6 Codable decodes from string representation",
|
||||
arguments: [
|
||||
"2001:db8::/32",
|
||||
"fe80::/10",
|
||||
"::1/128",
|
||||
]
|
||||
)
|
||||
func testCIDRv6CodableDecode(cidr: String) throws {
|
||||
let json = Data("\"\(cidr)\"".utf8)
|
||||
let decoded = try JSONDecoder().decode(CIDRv6.self, from: json)
|
||||
let expected = try CIDRv6(cidr)
|
||||
#expect(decoded == expected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,5 +427,36 @@ struct IPv4AddressTests {
|
||||
#expect(Bool(false), "Should have thrown IPAddressError, got: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test(
|
||||
"Codable encodes to string representation",
|
||||
arguments: [
|
||||
"127.0.0.1",
|
||||
"192.168.1.1",
|
||||
"0.0.0.0",
|
||||
"255.255.255.255",
|
||||
]
|
||||
)
|
||||
func testCodableEncode(address: String) throws {
|
||||
let original = try IPv4Address(address)
|
||||
let encoded = try JSONEncoder().encode(original)
|
||||
#expect(String(data: encoded, encoding: .utf8) == "\"\(address)\"")
|
||||
}
|
||||
|
||||
@Test(
|
||||
"Codable decodes from string representation",
|
||||
arguments: [
|
||||
"127.0.0.1",
|
||||
"192.168.1.1",
|
||||
"0.0.0.0",
|
||||
"255.255.255.255",
|
||||
]
|
||||
)
|
||||
func testCodableDecode(address: String) throws {
|
||||
let json = Data("\"\(address)\"".utf8)
|
||||
let decoded = try JSONDecoder().decode(IPv4Address.self, from: json)
|
||||
let expected = try IPv4Address(address)
|
||||
#expect(decoded == expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,4 +240,35 @@ struct IPv6AddressTests {
|
||||
"Address \(addressString) (\(description)) should\(expected ? "" : " not") be documentation"
|
||||
)
|
||||
}
|
||||
|
||||
@Test(
|
||||
"Codable encodes to string representation",
|
||||
arguments: [
|
||||
("::1", "::1"),
|
||||
("2001:db8::1", "2001:db8::1"),
|
||||
("::", "::"),
|
||||
("fe80::1", "fe80::1"),
|
||||
]
|
||||
)
|
||||
func testCodableEncode(input: String, expected: String) throws {
|
||||
let original = try IPv6Address(input)
|
||||
let encoded = try JSONEncoder().encode(original)
|
||||
#expect(String(data: encoded, encoding: .utf8) == "\"\(expected)\"")
|
||||
}
|
||||
|
||||
@Test(
|
||||
"Codable decodes from string representation",
|
||||
arguments: [
|
||||
"::1",
|
||||
"2001:db8::1",
|
||||
"::",
|
||||
"fe80::1",
|
||||
]
|
||||
)
|
||||
func testCodableDecode(address: String) throws {
|
||||
let json = Data("\"\(address)\"".utf8)
|
||||
let decoded = try JSONDecoder().decode(IPv6Address.self, from: json)
|
||||
let expected = try IPv6Address(address)
|
||||
#expect(decoded == expected)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user