Skip to content

Instantly share code, notes, and snippets.

@Kristijan22
Last active July 22, 2020 22:05
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 Kristijan22/b49febe96d36e57f6213d45e49f9ea2f to your computer and use it in GitHub Desktop.
Save Kristijan22/b49febe96d36e57f6213d45e49f9ea2f to your computer and use it in GitHub Desktop.
Modified AutoLenses stencil for Sourcery. Includes support for optional composition.
@testable import YourProjectName
// swiftlint:disable variable_name
struct Lens<Whole, Part> {
let get: (Whole) -> Part
let set: (Part, Whole) -> Whole
var affine: Affine<Whole, Part> {
return Affine<Whole, Part>(
tryGet: self.get,
trySet: self.set
)
}
/**
Compose operator for lenses that allows us to make changes to nested structs.
- Returns: Composition of two lenses
Example usage:
```
struct House {
let location: Location
...
}
struct Location {
let address: Address
...
}
struct Address {
let streetName: String
...
}
let someAddress = Address(streetName: "Broadway", ...)
let someLocation = Location(address: someAddress, ...)
let someHouse = House(location: someLocation, ...)
let houseLocationStreetLens = (House.locationLens * Location.addressLens * Address.streetNameLens)
houseLocationStreetLens.set("Hollywood Bld", someHouse)
```
*/
static func * <Subpart>(lhs: Lens<Whole, Part>, rhs: Lens<Part, Subpart>) -> Lens<Whole, Subpart> {
return Lens<Whole, Subpart>(
get: { whole in rhs.get(lhs.get(whole)) },
set: { subpart, whole in lhs.set(rhs.set(subpart, lhs.get(whole)), whole) }
)
}
static func * <Subpart>(lhs: Lens<Whole, Part>, rhs: Affine<Part, Subpart>) -> Affine<Whole, Subpart> {
return lhs.affine * rhs
}
static func * <Subpart>(lhs: Affine<Whole, Part>, rhs: Lens<Part, Subpart>) -> Affine<Whole, Subpart> {
return lhs * rhs.affine
}
static func * <Subpart>(lhs: Lens<Whole, Part?>, rhs: Lens<Part, Subpart>) -> Affine<Whole, Subpart> {
return lhs.affine * Part?.prism.affine * rhs.affine
}
}
struct Prism<Whole, Part> {
let tryGet: (Whole) -> Part?
let set: (Part) -> Whole
var affine: Affine<Whole, Part> {
return Affine<Whole, Part>(
tryGet: self.tryGet,
trySet: { part, _ in self.set(part) }
)
}
}
struct Affine<Whole, Part> {
let tryGet: (Whole) -> Part?
let trySet: (Part, Whole) -> Whole?
static func * <Subpart>(lhs: Affine<Whole, Part>, rhs: Affine<Part, Subpart>) -> Affine<Whole, Subpart> {
return Affine<Whole, Subpart>(
tryGet: { whole in lhs.tryGet(whole).flatMap(rhs.tryGet) },
trySet: { (subpart, whole) in lhs.tryGet(whole).flatMap { rhs.trySet(subpart, $0) }.flatMap { lhs.trySet($0, whole) } }
)
}
}
extension Optional {
static var prism: Prism<Optional,Wrapped> {
return Prism<Optional,Wrapped>.init(
tryGet: { $0 },
set: Optional.some)
}
}
{% for type in types.types|struct where type|annotated:"AutoLenses" %}
extension {{ type.name }} {
{% for variable in type.storedVariables %}
static let {{ variable.name }}Lens = Lens<{{type.name}}, {{variable.typeName}}>(
get: { $0.{{variable.name}} },
set: { {{variable.name}}, {{type.name|lowerFirstLetter}} in
{{type.name}}(
{% for argument in type.storedVariables %}
{{argument.name}}: {% if variable.name == argument.name %}{{variable.name}}{% else %} {{type.name|lowerFirstLetter}}.{{argument.name}}{% endif %}{{ ', ' if not forloop.last }}
{% endfor %})
}
){% endfor %}
}
{% endfor %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment