201 lines
5.9 KiB
Swift
201 lines
5.9 KiB
Swift
|
|
extension String {
|
|
|
|
func htmlEscaped() -> String {
|
|
replacingOccurrences(of: "&", with: "&")
|
|
.replacingOccurrences(of: "\"", with: """)
|
|
.replacingOccurrences(of: "'", with: "'")
|
|
.replacingOccurrences(of: "<", with: "<")
|
|
.replacingOccurrences(of: ">", with: ">")
|
|
}
|
|
|
|
func percentDecoded() -> String {
|
|
guard let decoded = removingPercentEncoding else {
|
|
return self
|
|
}
|
|
return decoded
|
|
}
|
|
|
|
var withLeadingSlashRemoved: String {
|
|
hasPrefix("/") ? String(dropFirst("/".count)) : self
|
|
}
|
|
|
|
var withLeadingSlash: String {
|
|
hasPrefix("/") ? self : "/" + self
|
|
}
|
|
|
|
var withTrailingSlashRemoved: String {
|
|
hasSuffix("/") ? String(dropLast("/".count)) : self
|
|
}
|
|
|
|
var withTrailingSlash: String {
|
|
hasSuffix("/") ? self : self + "/"
|
|
}
|
|
|
|
var withHttpPrefixRemoved: String {
|
|
if hasPrefix("https://") {
|
|
return String(dropFirst("https://".count))
|
|
}
|
|
if hasPrefix("http://") {
|
|
return String(dropFirst("http://".count))
|
|
}
|
|
return self
|
|
}
|
|
|
|
var removingSurroundingQuotes: String {
|
|
if hasPrefix("\"") && hasSuffix("\"") {
|
|
return dropBeforeFirst("\"").dropAfterLast("\"")
|
|
}
|
|
if hasPrefix("'") && hasSuffix("'") {
|
|
return dropBeforeFirst("'").dropAfterLast("'")
|
|
}
|
|
return self
|
|
}
|
|
|
|
var nonEmpty: String? {
|
|
isEmpty ? nil : self
|
|
}
|
|
|
|
var trimmed: String {
|
|
trimmingCharacters(in: .whitespacesAndNewlines)
|
|
}
|
|
|
|
func indented(by indentation: String) -> String {
|
|
components(separatedBy: "\n").joined(separator: "\n" + indentation)
|
|
}
|
|
|
|
var withoutEmptyLines: String {
|
|
components(separatedBy: "\n")
|
|
.filter { !$0.trimmed.isEmpty }
|
|
.joined(separator: "\n")
|
|
}
|
|
|
|
/**
|
|
Remove the part after the last occurence of the separator (including the separator itself).
|
|
|
|
The string is left unchanged, if it does not contain the separator.
|
|
*/
|
|
func dropAfterLast(_ separator: String) -> String {
|
|
guard contains(separator) else {
|
|
return self
|
|
}
|
|
return components(separatedBy: separator).dropLast().joined(separator: separator)
|
|
}
|
|
|
|
/**
|
|
Remove everything before the last separator.
|
|
|
|
Also removes the separator itself. If the separator is not contained in the string, then the full string is returned.
|
|
*/
|
|
func dropBeforeLast<T>(_ separator: T) -> String where T: StringProtocol {
|
|
components(separatedBy: separator).last!
|
|
}
|
|
|
|
func dropBeforeFirst(_ separator: String) -> String {
|
|
guard contains(separator) else {
|
|
return self
|
|
}
|
|
return components(separatedBy: separator).dropFirst().joined(separator: separator)
|
|
}
|
|
|
|
func lastComponentAfter(_ separator: String) -> String {
|
|
components(separatedBy: separator).last!
|
|
}
|
|
|
|
/**
|
|
Insert the new content before the last occurence of the specified separator.
|
|
|
|
If the separator does not appear in the string, then the new content is simply appended.
|
|
*/
|
|
func insert(_ content: String, beforeLast separator: String) -> String {
|
|
let parts = components(separatedBy: separator)
|
|
guard parts.count > 1 else {
|
|
return self + content
|
|
}
|
|
return parts.dropLast().joined(separator: separator) + content + separator + parts.last!
|
|
}
|
|
|
|
/**
|
|
Remove everything behind the first separator.
|
|
|
|
Also removes the separator itself. If the separator is not contained in the string, then the full string is returned.
|
|
*/
|
|
func dropAfterFirst<T>(_ separator: T) -> String where T: StringProtocol {
|
|
components(separatedBy: separator).first!
|
|
}
|
|
|
|
func between(_ start: String, and end: String) -> String {
|
|
dropBeforeFirst(start).dropAfterFirst(end)
|
|
}
|
|
|
|
/**
|
|
Split the string at the first occurence of the separator
|
|
|
|
If the String does not contain the separator, then `before` will contain the whole string, and `after` will be empty
|
|
*/
|
|
func splitAtFirst(_ separator: String) -> (before: String, after: String) {
|
|
let parts = components(separatedBy: separator)
|
|
return (parts.first!, parts.dropFirst().joined(separator: separator))
|
|
}
|
|
}
|
|
|
|
extension String {
|
|
|
|
func removingPrefix(_ prefix: String) -> String? {
|
|
guard self.hasPrefix(prefix) else { return nil }
|
|
return String(self.dropFirst(prefix.count))
|
|
}
|
|
}
|
|
|
|
extension String {
|
|
|
|
var fileNameWithoutExtension: String {
|
|
dropAfterLast(".")
|
|
}
|
|
|
|
var fileExtension: String? {
|
|
let parts = components(separatedBy: ".")
|
|
guard parts.count > 1 else { return nil }
|
|
return parts.last
|
|
}
|
|
|
|
var fileNameAndExtension: (fileName: String, fileExtension: String?) {
|
|
let parts = components(separatedBy: ".")
|
|
guard parts.count > 1 else {
|
|
return (self, nil)
|
|
}
|
|
return (fileName: parts.dropLast().joined(separator: "."), fileExtension: parts.last)
|
|
}
|
|
}
|
|
|
|
extension Substring {
|
|
|
|
func dropBeforeFirst(_ separator: String) -> String {
|
|
guard contains(separator) else {
|
|
return String(self)
|
|
}
|
|
return components(separatedBy: separator).dropFirst().joined(separator: separator)
|
|
}
|
|
|
|
func between(_ start: String, and end: String) -> String {
|
|
components(separatedBy: start).last!
|
|
.components(separatedBy: end).first!
|
|
}
|
|
|
|
func between(first: String, andLast last: String) -> String {
|
|
dropBeforeFirst(first).dropAfterLast(last)
|
|
}
|
|
|
|
func last(after: String) -> String {
|
|
components(separatedBy: after).last!
|
|
}
|
|
|
|
/**
|
|
Split the string at the first occurence of the separator
|
|
*/
|
|
func splitAtFirst(_ separator: String) -> (String, String) {
|
|
let parts = components(separatedBy: separator)
|
|
return (parts.first!, parts.dropFirst().joined(separator: separator))
|
|
}
|
|
}
|