Last active
September 10, 2019 02:11
-
-
Save kitasuke/ee73934c2eda0f63bbd371b69380c443 to your computer and use it in GitHub Desktop.
Experimental code for SwiftPM with Function Builders. Declarative package description for SwiftPM.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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