78 lines
3.0 KiB
Swift
78 lines
3.0 KiB
Swift
import Foundation
|
|
|
|
struct SwiftCodeGenerator {
|
|
private let fileContentReader: FileContentReader
|
|
private let indentationUnit: String
|
|
|
|
init(
|
|
fileContentReader: FileContentReader = FileContentReader(),
|
|
indentationUnit: String = " "
|
|
) {
|
|
self.fileContentReader = fileContentReader
|
|
self.indentationUnit = indentationUnit
|
|
}
|
|
|
|
func generate(from rootNamespace: NamespaceNode) throws -> String {
|
|
generatedFileBanner() + (try renderNamespace(rootNamespace, depth: 0))
|
|
}
|
|
}
|
|
|
|
private extension SwiftCodeGenerator {
|
|
func generatedFileBanner() -> String {
|
|
"""
|
|
// Generated by the Embedder plugin from the "Static Inline" directory.
|
|
// Any manual changes will be overwritten on the next build.
|
|
|
|
""" + "\n"
|
|
}
|
|
|
|
func renderNamespace(_ namespace: NamespaceNode, depth: Int) throws -> String {
|
|
let openingLine = namespaceOpeningLine(for: namespace, depth: depth)
|
|
let bodyLines = try renderBody(of: namespace, depth: depth + 1)
|
|
let closingLine = namespaceClosingLine(depth: depth)
|
|
return openingLine + bodyLines + closingLine
|
|
}
|
|
|
|
func namespaceOpeningLine(for namespace: NamespaceNode, depth: Int) -> String {
|
|
"\(indent(depth: depth))enum \(namespace.name) {\n"
|
|
}
|
|
|
|
func namespaceClosingLine(depth: Int) -> String {
|
|
"\(indent(depth: depth))}\n"
|
|
}
|
|
|
|
func renderBody(of namespace: NamespaceNode, depth: Int) throws -> String {
|
|
let fileDeclarations = try namespace.files.map { try renderFileDeclaration($0, depth: depth) }
|
|
let nestedNamespaces = try namespace.subNamespaces.map { try renderNamespace($0, depth: depth) }
|
|
return joinWithBlankLines(fileDeclarations + nestedNamespaces)
|
|
}
|
|
|
|
func renderFileDeclaration(_ file: EmbeddableFile, depth: Int) throws -> String {
|
|
let propertyName = IdentifierSanitizer.propertyName(fromFilename: file.filename)
|
|
let literal = try renderStringLiteral(for: file, baseIndent: indent(depth: depth))
|
|
return "\(indent(depth: depth))static let \(propertyName): String = \(literal)\n"
|
|
}
|
|
|
|
func renderStringLiteral(for file: EmbeddableFile, baseIndent: String) throws -> String {
|
|
let content = try fileContentReader.readTextContent(of: file)
|
|
let rawLiteral = StringLiteralEscaper.rawTripleQuotedLiteral(from: content)
|
|
return indentContinuationLines(of: rawLiteral, by: baseIndent)
|
|
}
|
|
|
|
func indentContinuationLines(of literal: String, by baseIndent: String) -> String {
|
|
let lines = literal.components(separatedBy: "\n")
|
|
guard lines.count > 1 else { return literal }
|
|
let firstLine = lines[0]
|
|
let continuationLines = lines.dropFirst().map { "\(baseIndent)\($0)" }
|
|
return ([firstLine] + continuationLines).joined(separator: "\n")
|
|
}
|
|
|
|
func joinWithBlankLines(_ declarations: [String]) -> String {
|
|
declarations.joined(separator: "\n")
|
|
}
|
|
|
|
func indent(depth: Int) -> String {
|
|
String(repeating: indentationUnit, count: depth)
|
|
}
|
|
}
|