Skip to content

Instantly share code, notes, and snippets.

@kitasuke
Last active September 10, 2019 02:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kitasuke/ee73934c2eda0f63bbd371b69380c443 to your computer and use it in GitHub Desktop.
Save kitasuke/ee73934c2eda0f63bbd371b69380c443 to your computer and use it in GitHub Desktop.
Experimental code for SwiftPM with Function Builders. Declarative package description for SwiftPM.
import Foundation
@_functionBuilder
struct ContentListBuilder {
static func buildBlock() -> ContentList {
return EmptyContentList()
}
static func buildBlock(_ list: ContentList) -> ContentList {
return list
}
static func buildBlock(_ lists: ContentList...) -> ContentList {
return TupleContentList(lists)
}
}
@_functionBuilder
struct ContentBuilder {
static func buildBlock() -> Content {
return EmptyContent()
}
static func buildBlock(_ content: Content) -> Content {
return content
}
static func buildBlock(_ contents: Content...) -> Content {
return TupleContent(contents)
}
}
protocol Content: Encodable {}
extension Content {
func getAll<T>() -> [T] {
switch self {
case let tuple as TupleContent:
return (tuple.values as? [T]) ?? []
case let content as T:
return [content]
default:
return []
}
}
}
protocol ContentList: Content {
var content: Content { get }
}
extension ContentList {
var all: [ContentList] {
switch self {
case let tuple as TupleContentList:
return tuple.values
case let list as ContentList:
return [list]
default:
return []
}
}
}
struct EmptyContent: Content {}
struct EmptyContentList: ContentList {
var content: Content { EmptyContent() }
}
protocol Tuple {
associatedtype Body
var values: [Body] { get }
init(_ values: [Body])
}
struct TupleContent: Tuple, Content {
let values: [Content]
init(_ values: [Content]) {
self.values = values
}
}
extension TupleContent {
func encode(to encoder: Encoder) throws {}
}
struct TupleContentList: Tuple, ContentList {
var content: Content {
return self
}
let values: [ContentList]
init(_ values: [ContentList]) {
self.values = values
}
}
extension TupleContentList {
func encode(to encoder: Encoder) throws {}
}
struct Package: Content {
enum CodingKeys: CodingKey {
case name
case products
case dependencies
case targets
}
let name: String
let contentList: ContentList
init(name: String, @ContentListBuilder builder: () -> ContentList) {
self.name = name
self.contentList = builder()
}
}
extension Package {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
let lists = contentList.all
for list in lists {
switch list {
case let productList as ProductList:
try container.encode(productList, forKey: .products)
case let dependencyList as DependencyList:
try container.encode(dependencyList, forKey: .dependencies)
case let targetList as TargetList:
try container.encode(targetList, forKey: .targets)
default:
break
}
}
}
}
struct ProductList: ContentList {
let content: Content
init(@ContentBuilder builder: () -> Content) {
self.content = builder()
}
}
extension ProductList {
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let products: [Product] = content.getAll()
try container.encode(contentsOf: products)
}
}
struct Product: Content {
enum CodingKeys: CodingKey {
case name
case type
case targets
}
enum `Type`: String, Codable {
case executable, `static`, dynamic
}
let name: String
let type: Type
let targetList: ContentList
init(name: String, type: Type = .executable, @ContentListBuilder builder: () -> ContentList = { EmptyContentList() }) {
self.name = name
self.type = type
self.targetList = builder()
}
}
extension Product {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(type, forKey: .type)
try container.encodeIfPresent(targetList as? TargetList, forKey: .targets)
}
}
struct TargetList: ContentList {
let content: Content
init(@ContentBuilder builder: () -> Content) {
self.content = builder()
}
}
extension TargetList {
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let targets: [Target] = content.getAll()
try container.encode(contentsOf: targets)
}
}
struct Target: Content {
enum CodingKeys: CodingKey {
case name
case dependencies
}
let name: String
let dependencyList: ContentList
init(name: String, @ContentListBuilder builder: () -> ContentList = { EmptyContentList() }) {
self.name = name
self.dependencyList = builder()
}
}
extension Target {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encodeIfPresent(dependencyList as? TargetDependencyList, forKey: .dependencies)
}
}
struct TargetDependencyList: ContentList {
let content: Content
init(@ContentBuilder builder: () -> Content) {
self.content = builder()
}
}
extension TargetDependencyList {
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let dependencies: [Content] = content.getAll()
for dependency in dependencies {
switch dependency {
case let value as TargetDependency:
try container.encode(value)
case let value as Target:
try container.encode(value)
case let value as Product:
try container.encode(value)
default:
break
}
}
}
}
struct TargetDependency: Content {
let name: String
}
struct DependencyList: ContentList {
let content: Content
init(@ContentBuilder builder: () -> Content = { EmptyContent() }) {
self.content = builder()
}
}
extension DependencyList {
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let dependencies: [Dependency] = content.getAll()
try container.encode(contentsOf: dependencies)
}
}
struct Dependency: Content {
enum CodingKeys: CodingKey {
case url
case from
case exact
}
var url: String?
var from: String?
var exact: String?
init(url: String) {
self.url = url
}
init(url: String, from: String) {
self.url = url
self.from = from
}
init(url: String, exact: String) {
self.url = url
self.exact = exact
}
}
extension Dependency {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(url, forKey: .url)
try container.encodeIfPresent(from, forKey: .from)
try container.encodeIfPresent(exact, forKey: .exact)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment