swift-sdk/Sources/MCP/Extensions/Data+Extensions.swift
2025-02-11 15:22:35 -08:00

87 lines
3.1 KiB
Swift

import Foundation
import RegexBuilder
extension Data {
/// Regex pattern for data URLs
@inline(__always) private static var dataURLRegex:
Regex<(Substring, Substring, Substring?, Substring)>
{
Regex {
"data:"
Capture {
ZeroOrMore(.reluctant) {
CharacterClass.anyOf(",;").inverted
}
}
Optionally {
";charset="
Capture {
OneOrMore(.reluctant) {
CharacterClass.anyOf(",;").inverted
}
}
}
Optionally { ";base64" }
","
Capture {
ZeroOrMore { .any }
}
}
}
/// Checks if a given string is a valid data URL.
///
/// - Parameter string: The string to check.
/// - Returns: `true` if the string is a valid data URL, otherwise `false`.
/// - SeeAlso: [RFC 2397](https://www.rfc-editor.org/rfc/rfc2397.html)
public static func isDataURL(string: String) -> Bool {
return string.wholeMatch(of: dataURLRegex) != nil
}
/// Parses a data URL string into its MIME type and data components.
///
/// - Parameter string: The data URL string to parse.
/// - Returns: A tuple containing the MIME type and decoded data, or `nil` if parsing fails.
/// - SeeAlso: [RFC 2397](https://www.rfc-editor.org/rfc/rfc2397.html)
public static func parseDataURL(_ string: String) -> (mimeType: String, data: Data)? {
guard let match = string.wholeMatch(of: dataURLRegex) else {
return nil
}
// Extract components using strongly typed captures
let (_, mediatype, charset, encodedData) = match.output
let isBase64 = string.contains(";base64,")
// Process MIME type
var mimeType = mediatype.isEmpty ? "text/plain" : String(mediatype)
if let charset = charset, !charset.isEmpty, mimeType.starts(with: "text/") {
mimeType += ";charset=\(charset)"
}
// Decode data
let decodedData: Data
if isBase64 {
guard let base64Data = Data(base64Encoded: String(encodedData)) else { return nil }
decodedData = base64Data
} else {
guard
let percentDecodedData = String(encodedData).removingPercentEncoding?.data(
using: .utf8)
else { return nil }
decodedData = percentDecodedData
}
return (mimeType: mimeType, data: decodedData)
}
/// Encodes the data as a data URL string with an optional MIME type.
///
/// - Parameter mimeType: The MIME type of the data. If `nil`, "text/plain" will be used.
/// - Returns: A data URL string representation of the data.
/// - SeeAlso: [RFC 2397](https://www.rfc-editor.org/rfc/rfc2397.html)
public func dataURLEncoded(mimeType: String? = nil) -> String {
let base64Data = self.base64EncodedString()
return "data:\(mimeType ?? "text/plain");base64,\(base64Data)"
}
}