Scaffold v1.0.0
This commit is contained in:
77
Sources/EmbedderTool/SwiftCodeGenerator.swift
Normal file
77
Sources/EmbedderTool/SwiftCodeGenerator.swift
Normal file
@@ -0,0 +1,77 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user