import Foundation extension String { var nonEmpty: String? { self.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 unchanges, 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) } 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! } func dropAfterFirst(_ separator: T) -> String where T: StringProtocol { components(separatedBy: separator).first! } func between(_ start: String, and end: String) -> String { dropBeforeFirst(start).dropAfterFirst(end) } } extension Substring { func between(_ start: String, and end: String) -> String { components(separatedBy: start).last! .components(separatedBy: end).first! } } extension String { func createFolderAndWrite(to url: URL) throws { try data(using: .utf8)!.createFolderAndWrite(to: url) } }