Skip to content

Instantly share code, notes, and snippets.

@AliSoftware
Last active June 20, 2020 11:38
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save AliSoftware/691eee22464b6f9e1796a85111a9c2ee to your computer and use it in GitHub Desktop.
Save AliSoftware/691eee22464b6f9e1796a85111a9c2ee to your computer and use it in GitHub Desktop.
enum Demo {
case simple
case oneValue(Int)
case twoValues(String, Double)
case threeValues(one: Int, two: Float, [Int])
}
//: # Direct exposition in the enum
//: ## Sourcery Template
/* enum-values.stencil
{% for enum in types.enums %}
{{ enum.accessLevel }} extension {{enum.localName}} {
{% for case in enum.cases %}
{% if case.hasAssociatedValue %}
{% set returnType %}{% filter removeNewlines:leading %}
{% if case.associatedValues.count == 1 %}
{{case.associatedValues.first.typeName}}
{% else %}
({% for av in case.associatedValues %}
{% if av.localName %}{{av.localName}}: {% endif %}{{av.typeName}}{% if not forloop.last %}, {% endif %}
{% endfor %})
{% endif %}
{% endfilter %}{% endset %}
var {{case.name}}: {{returnType}}? {
guard case let .{{case.name}}(value) = self else { return nil }
return value
}
{% else %}
var {{case.name}}: Void? {
guard case .{{case.name}} = self else { return nil }
return ()
}
{% endif %}
var is{{case.name|upperFirstLetter}}: Bool {
return {{case.name}} != nil
}
{% endfor %}
}
{% endfor %}
*/
//: ## Sourcery Generated code
internal extension Demo {
var simple: Void? {
guard case .simple = self else { return nil }
return ()
}
var isSimple: Bool {
return simple != nil
}
var oneValue: Int? {
guard case let .oneValue(value) = self else { return nil }
return value
}
var isOneValue: Bool {
return oneValue != nil
}
var twoValues: (String,Double)? {
guard case let .twoValues(value) = self else { return nil }
return value
}
var isTwoValues: Bool {
return twoValues != nil
}
var threeValues: (one:Int,two:Float,[Int])? {
guard case let .threeValues(value) = self else { return nil }
return value
}
var isThreeValues: Bool {
return threeValues != nil
}
}
//: ## Demo 1
let d1 = Demo.simple
let d2 = Demo.oneValue(42)
let d3 = Demo.twoValues("Hello", 13.37)
let d4 = Demo.threeValues(one: 42, two: 13.37, [1,2,3])
let allExamples = ["d1": d1, "d2": d2, "d3": d3, "d4": d4]
for (name, sut) in allExamples {
print("== \(name) ==")
print(" - simple: \(sut.isSimple) = \(String(describing: sut.simple))")
print(" - oneValue: \(sut.isOneValue) = \(String(describing: sut.oneValue))")
print(" - twoValues: \(sut.isTwoValues) = \(String(describing: sut.twoValues))")
print(" - threeValues: \(sut.isThreeValues) = \(String(describing: sut.threeValues))")
}
d4.threeValues!.one // 42
d4.threeValues!.two // 13.37
d4.threeValues!.2 // [1, 2, 3]
enum Demo {
case simple
case oneValue(Int)
case twoValues(String, Double)
case threeValues(one: Int, two: Float, [Int])
}
//: # Indirect exposition via `is` and `as` properties
//: ## Sourcery Template
/* enum-values-2.stencil
{% macro returnType case %}{% filter removeNewlines:leading %}
{% if case.associatedValues.count == 1 %}
{{case.associatedValues.first.typeName}}
{% else %}
({% for av in case.associatedValues %}
{% if av.localName %}{{av.localName}}: {% endif %}{{av.typeName}}{% if not forloop.last %}, {% endif %}
{% endfor %})
{% endif %}
{% endfilter %}{% endmacro %}
{% for enum in types.enums %}
{{ enum.accessLevel }} extension {{enum.localName}} {
struct EnumAssociatedValues {
fileprivate base: {{enum.localName}}
{% for case in enum.cases %}{% if case.hasAssociatedValue %}
var {{case.name}}: {% call returnType case %}? {
guard case let .{{case.name}}(value) = base else { return nil }
return value
}
{% else %}
var {{case.name}}: Void? {
guard case .{{case.name}} = base else { return nil }
return ()
}
{% endif %}{% endfor %}
}
var `as`: EnumAssociatedValues { .init(base: self) }
struct EnumIdentity {
fileprivate base: {{enum.localName}}.EnumAssociatedValue
{% for case in enum.cases %}
var {{case.name}}: Bool {
return base.{{case.name}} != nil
}
{% endfor %}
}
var `is`: EnumIdentity { .init(base: as) }
}
{% endfor %}
*/
//: ## Sourcery Generated Code
internal extension Demo {
struct EnumAssociatedValues {
fileprivate let base: Demo
var simple: Void? {
guard case .simple = base else { return nil }
return ()
}
var oneValue: Int? {
guard case let .oneValue(value) = base else { return nil }
return value
}
var twoValues: (String,Double)? {
guard case let .twoValues(value) = base else { return nil }
return value
}
var threeValues: (one:Int,two:Float,[Int])? {
guard case let .threeValues(value) = base else { return nil }
return value
}
}
var `as`: EnumAssociatedValues { .init(base: self) }
struct EnumIdentity {
fileprivate let base: Demo.EnumAssociatedValues
var simple: Bool {
return base.simple != nil
}
var oneValue: Bool {
return base.oneValue != nil
}
var twoValues: Bool {
return base.twoValues != nil
}
var threeValues: Bool {
return base.threeValues != nil
}
}
var `is`: EnumIdentity { .init(base: `as`) }
}
//: ## Demo 2
let d1 = Demo.simple
let d2 = Demo.oneValue(42)
let d3 = Demo.twoValues("Hello", 13.37)
let d4 = Demo.threeValues(one: 42, two: 13.37, [1,2,3])
let allExamples = ["d1": d1, "d2": d2, "d3": d3, "d4": d4]
for (name, sut) in allExamples {
print("== \(name) ==")
print(" - simple: \(sut.is.simple) = \(String(describing: sut.as.simple))")
print(" - oneValue: \(sut.is.oneValue) = \(String(describing: sut.as.oneValue))")
print(" - twoValues: \(sut.is.twoValues) = \(String(describing: sut.as.twoValues))")
print(" - threeValues: \(sut.is.threeValues) = \(String(describing: sut.as.threeValues))")
}
d4.threeValues!.one // 42
d4.threeValues!.two // 13.37
d4.threeValues!.2 // [1, 2, 3]
@AliSoftware
Copy link
Author

Console output:

== d2 ==
 - simple:      false = nil
 - oneValue:    true = Optional(42)
 - twoValues:   false = nil
 - threeValues: false = nil
== d1 ==
 - simple:      true = Optional(())
 - oneValue:    false = nil
 - twoValues:   false = nil
 - threeValues: false = nil
== d3 ==
 - simple:      false = nil
 - oneValue:    false = nil
 - twoValues:   true = Optional(("Hello", 13.37))
 - threeValues: false = nil
== d4 ==
 - simple:      false = nil
 - oneValue:    false = nil
 - twoValues:   false = nil
 - threeValues: true = Optional((one: 42, two: 13.37, [1, 2, 3]))

@AliSoftware
Copy link
Author

Simplified version of the demo without the implementation details getting in the way:

enum Demo {
    case simple
    case oneValue(Int)
    case twoValues(String, Double)
    case threeValues(one: Int, two: Float, [Int])
}

let d1 = Demo.simple
let d2 = Demo.oneValue(42)
let d3 = Demo.twoValues("Hello", 13.37)
let d4 = Demo.threeValues(one: 42, two: 13.37, [1,2,3])

d1.is.simple // true
d1.is.oneValue // false
d2.is.oneValue // true
d2.as.oneValue // Optional(42)
d3.as.oneValue // nil
d4.as.threeValues!.one // 42
d4.as.threeValues!.two // 13.37
d4.as.threeValues!.2   // [1, 2, 3]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment