Skip to content

Commit

Permalink
Refactor: organize project into separate modules
Browse files Browse the repository at this point in the history
  • Loading branch information
yhkaplan committed May 4, 2020
1 parent 19db5eb commit 2649064
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 159 deletions.
15 changes: 10 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import PackageDescription

let package = Package(
name: "scaffold", // TODO: move all deps and real code to ScaffoldKit
name: "Scaffold",
products: [
.executable(name: "scaffold", targets: ["Scaffold"]),
.library(name: "ScaffoldKit", targets: ["ScaffoldKit"]),
],
dependencies: [
.package(url: "https://github.com/SwiftGen/StencilSwiftKit.git", from: "2.7.2"),
.package(url: "https://github.com/jpsim/Yams.git", from: "3.0.0"),
Expand All @@ -13,16 +17,17 @@ let package = Package(
// TODO: add this to automatically add to xcodeproj? https://github.com/tuist/XcodeProj.git
],
targets: [
.target(name: "Scaffold", dependencies: ["ScaffoldKit"]),
.target(
name: "scaffold",
name: "ScaffoldKit",
dependencies: [
.product(name: "StencilSwiftKit", package: "StencilSwiftKit"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Yams", package: "Yams"),
.product(name: "PathKit", package: "PathKit"),
]),
]),
.testTarget(
name: "scaffoldTests",
dependencies: ["scaffold"]),
name: "ScaffoldKitTests",
dependencies: ["ScaffoldKit"]),
]
)
58 changes: 58 additions & 0 deletions Sources/ScaffoldKit/Config.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Yams
import Foundation

struct TemplateConfig {
var name: String
var templatePath: String
var outputPath: String?
}

extension TemplateConfig: Decodable {
enum CodingKeys: String, CodingKey {
case name, templatePath, outputPath
}

init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
name = try c.decode(String.self, forKey: .name)
templatePath = try c.decode(String.self, forKey: .templatePath)
outputPath = try c.decodeIfPresent(String.self, forKey: .outputPath)
}
}

struct TemplateGroupConfig {
var name: String
var templateNames: [String]
/// Overrides template output path
var outputPath: String?
}

extension TemplateGroupConfig: Decodable {
enum CodingKeys: String, CodingKey {
case name, templateNames, outputPath
}

init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
name = try c.decode(String.self, forKey: .name)
templateNames = try c.decode([String].self, forKey: .templateNames)
outputPath = try c.decodeIfPresent(String.self, forKey: .outputPath)
}
}

struct Config {
var templates: [TemplateConfig]
var groups: [TemplateGroupConfig]
}

extension Config: Decodable {
enum CodingKeys: String, CodingKey {
case templates, groups
}

init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
templates = try c.decode([TemplateConfig].self, forKey: .templates)
groups = try c.decodeIfPresent([TemplateGroupConfig].self, forKey: .groups) ?? []
}
}
19 changes: 19 additions & 0 deletions Sources/ScaffoldKit/FileWriter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// FileWriter.swift
// ArgumentParser
//
// Created by josh on 2020/05/04.
//

import Foundation
import PathKit

protocol FileWritable {
func writeFile(_ string: String, to path: Path) throws
}
struct FileWriter: FileWritable {
func writeFile(_ string: String, to path: Path) throws {
print(string)
// try path.write(string) // TODO: actually write
}
}
92 changes: 92 additions & 0 deletions Sources/ScaffoldKit/Scaffold.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// Scaffold.swift
// ArgumentParser
//
// Created by josh on 2020/05/04.
//

import Stencil
import StencilSwiftKit
import ArgumentParser
import Foundation
import PathKit
import Yams

// TODO: break up into cleaner units, importing on ArgumentParser
public struct Scaffold: ParsableCommand {

// MARK: - Types

public enum ScaffoldError: String, LocalizedError {
case noTemplates = "No templates found or specified!"
public var errorDescription: String? { rawValue }
}

// MARK: - Flags

@Flag(help: "Print the output without writing the file(s) to disk. Default is false.")
var dryRun: Bool // TODO: can these be private?

// MARK: - Options

@Option(help: "Path to specific template or folder of templates. Default is ./Templates/")
var filePath: String?

@Option(help: "List of templates to generate from the config file")
var templates: String? // TODO: can't use array?

@Option(help: "Path to config file. Default is ./scaffold.yml")
var configFilePath: String?

@Option(help: "Path to output folder(s). Default is current directory.")
var outputPath: String?

@Option(help: "Value to pass to the name variable in the stencil template")
var name: String?

@Option(help: """
String with context values to pass to template (overrides name). More info here: <link>
Example: ``
""")
var context: String?

public init() {}

public func run() throws {
let filePath = self.filePath ?? "Templates/"
let templateNames = (self.templates ?? "./")
.split(separator: ",")
.map { String($0) + ".stencil" }
let configFilePath = Path(self.configFilePath ?? "scaffold.yml")
let outputPath = Path(self.outputPath ?? "./")

let context: [String: Any]?
if let name = name {
context = ["name": name]
// } else if let context = self.context { // TODO:
// // TODO: parse context w/ parser combinator
} else {
context = nil
}

let configFile: String = try configFilePath.read()
let objects = try YAMLDecoder().decode(Config.self, from: configFile)
dump(objects)

let path = Path(filePath)
let loader = FileSystemLoader(paths: [path])
let env = Environment(loader: loader)

if templateNames.isEmpty { throw ScaffoldError.noTemplates }

for templateName in templateNames {
let template = try env.renderTemplate(name: templateName, context: context)
if dryRun {
print(template)
} else {
// write to disk
}
}
}

}
155 changes: 1 addition & 154 deletions Sources/scaffold/main.swift
Original file line number Diff line number Diff line change
@@ -1,156 +1,3 @@
import Stencil
import StencilSwiftKit
import ArgumentParser
import PathKit
import Yams
import Foundation

struct TemplateConfig {
var name: String
var templatePath: String
var outputPath: String?
}

extension TemplateConfig: Decodable {
enum CodingKeys: String, CodingKey {
case name, templatePath, outputPath
}

init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
name = try c.decode(String.self, forKey: .name)
templatePath = try c.decode(String.self, forKey: .templatePath)
outputPath = try c.decodeIfPresent(String.self, forKey: .outputPath)
}
}

struct TemplateGroupConfig {
var name: String
var templateNames: [String]
/// Overrides template output path
var outputPath: String?
}

extension TemplateGroupConfig: Decodable {
enum CodingKeys: String, CodingKey {
case name, templateNames, outputPath
}

init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
name = try c.decode(String.self, forKey: .name)
templateNames = try c.decode([String].self, forKey: .templateNames)
outputPath = try c.decodeIfPresent(String.self, forKey: .outputPath)
}
}

struct Config {
var templates: [TemplateConfig]
var groups: [TemplateGroupConfig]
}

extension Config: Decodable {
enum CodingKeys: String, CodingKey {
case templates, groups
}

init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
templates = try c.decode([TemplateConfig].self, forKey: .templates)
groups = try c.decodeIfPresent([TemplateGroupConfig].self, forKey: .groups) ?? []
}
}

protocol FileWritable {
func writeFile(_ string: String)
}
struct FileWriter: FileWritable {
func writeFile(_ string: String) {
// TODO: just print to stdout for now
print(string)
}
}

struct Scaffold: ParsableCommand {

// MARK: - Types

enum ScaffoldError: String, LocalizedError {
case noTemplates = "No templates found or specified!"
var errorDescription: String? { rawValue }
}

// MARK: - Flags

@Flag(help: "Print the output without writing the file(s) to disk. Default is false.")
var dryRun: Bool // TODO: can these be private?

// MARK: - Options

@Option(help: "Path to specific template or folder of templates. Default is ./Templates/")
var filePath: String?

@Option(help: "List of templates to generate from the config file")
var templates: String? // TODO: can't use array?

@Option(help: "Path to config file. Default is ./scaffold.yml")
var configFilePath: String?

@Option(help: "Path to output folder(s). Default is current directory.")
var outputPath: String?

@Option(help: "Value to pass to the name variable in the stencil template")
var name: String?

@Option(help: """
String with context values to pass to template (overrides name). More info here: <link>
Example: ``
""")
var context: String?

// private let fileWriter: FileWritable
//
// init(fileWriter: FileWritable) {
// self.fileWriter = fileWriter
// }

func run() throws {
let filePath = self.filePath ?? "Templates/"
let templateNames = (self.templates ?? "./")
.split(separator: ",")
.map { String($0) + ".stencil" }
let configFilePath = Path(self.configFilePath ?? "scaffold.yml")
let outputPath = self.outputPath ?? "./"

let context: [String: Any]?
if let name = name {
context = ["name": name]
// } else if let context = self.context { // TODO:
// // TODO: parse context w/ parser combinator
} else {
context = nil
}

let configFile: String = try configFilePath.read()
let objects = try YAMLDecoder().decode(Config.self, from: configFile)
dump(objects)

// TODO: use loader field from Environment to load templates: FileSystemLoader
let path = Path(filePath)
let loader = FileSystemLoader(paths: [path])
// let loader = DictionaryLoader(templates: ["template": t])
let env = Environment(loader: loader)
if templateNames.isEmpty { throw ScaffoldError.noTemplates }
for templateName in templateNames {
let template = try env.renderTemplate(name: templateName, context: context)
if dryRun {
print(template)
} else {
// write to disk
}
}
}

}
import ScaffoldKit

Scaffold.main()
File renamed without changes.
File renamed without changes.

0 comments on commit 2649064

Please sign in to comment.