Skip to content

Commit 22f9a34

Browse files
committed
Add methods for encoding and decoding full domains
1 parent 20bd4b7 commit 22f9a34

File tree

1 file changed

+37
-0
lines changed

1 file changed

+37
-0
lines changed

Sources/ATSyntaxTools/Helpers/Punycode.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,43 @@ public enum Punycode {
3535
/// Delimiter used in Punycode to separate basic and encoded code points.
3636
private static let labelDelimiter: Character = "-"
3737

38+
/// Encodes a full domain name (e.g. `bücher.com` or `🍿.com`) using IDNA rules.
39+
///
40+
/// This function splits the domain into labels, encodes each with Punycode if needed,
41+
/// and prepends "xn--" to any encoded label as per IDNA.
42+
///
43+
/// - Parameter domain: A full domain name (may include Unicode).
44+
/// - Returns: The encoded domain (e.g. "xn--bcher-kva.com")
45+
public static func encodeDomain(_ domain: String) throws -> String {
46+
return try domain
47+
.split(separator: ".")
48+
.map { label in
49+
if label.allSatisfy(\.isASCII) {
50+
return String(label)
51+
} else {
52+
let encoded = try Punycode.encode(String(label))
53+
return "xn--" + encoded
54+
}
55+
}.joined(separator: ".")
56+
}
57+
58+
/// Decodes a full Punycode-based domain back to its Unicode representation.
59+
///
60+
/// - Parameter domain: A domain name with potential "xn--" labels.
61+
/// - Returns: A Unicode-decoded domain name.
62+
public static func decodeDomain(_ domain: String) throws -> String {
63+
return try domain
64+
.split(separator: ".")
65+
.map { label in
66+
if label.hasPrefix("xn--") {
67+
let punycode = String(label.dropFirst(4))
68+
return try Punycode.decode(punycode)
69+
} else {
70+
return String(label)
71+
}
72+
}.joined(separator: ".")
73+
}
74+
3875
/// Decodes a Punycode string into a Unicode string.
3976
///
4077
/// - Parameter punycode: The Punycode-encoded string (must be ASCII only).

0 commit comments

Comments
 (0)