carson-katri / awesome-result-builders

A list of cool DSLs made with Swift 5.4’s @resultBuilder
1.07k stars 45 forks source link
dsl functionbuilder resultbuilder swift swiftui

awesome-result-builders

A list of cool DSLs made with Swift 5.4’s @resultBuilder

As of Swift 5.4, functionBuilder has been renamed to resultBuilder

Feel free to contribute if you make or find something awesome.

Contents

Guides

A list of helpful guides/tutorials on result builders

Data

Data {
  [UInt8(0)]
  UInt8(1)
  Int8(2)
  "\u{03}"
  Int16(1284)
  if dataClause {
    CustomData()
  }
}

Array<Byte> {
  0b1010_1010
  0b1100_1100
  UInt8(32)
  [0x05, 0x06]
  Byte(0x01)
}
let condition: Bool = all {
    any {
        conditionA
        conditionB
            .inverted

        either {
            conditionC
        } or: {
            conditionD
        }
    }
    conditionE
}

Dependency Injection

DIContainer.register {
  New(MediaPlayer() as MediaplayerProtocol)
  New { _, id in ArticleViewModel(id: id) as PageViewModelProtocol }
  Shared(Router.init, as: RouterProtocol.self, DeeplinkHandler.self)
}

GraphQL

Operation(.query) {
  Add(\.country, alias: "canada") {
    Add(\.name)
    Add(\.continent) {
      Add(\.name)
    }
  }.code("CA")
}
query("") {
  hero {
    \.name
    lens(\.friends) {
      \.name
    }
  }
}
Schema<StarWarsAPI, StarWarsStore> {
  Enum(Episode.self) {
    Value(.newHope)
      .description("Released in 1977.")
    Value(.empire)
      .description("Released in 1980.")
    Value(.jedi)
      .description("Released in 1983.")
  }
  .description("One of the films in the Star Wars Trilogy.")
  ...
}
Weave(.query) {
    Object(Post.self) {
        Field(Post.CodingKeys.title)

        Object(Post.CodingKeys.author) {
            Field(Author.CodingKeys.id)
            Field(Author.CodingKeys.name)
                .argument(key: "lastName", value: "Doe")
        }

        Object(Post.CodingKeys.comments) {
            Field(Comment.CodingKeys.id)
            Field(Comment.CodingKeys.content)
        }
        .argument(key: "filter", value: CommentFilter.recent)
    }
}

HTML

html(lang: "en-US") {
  body(customData: ["hello":"world"]) {
    article(classes: "readme", "modern") {
      h1 {
        "Hello World"
      }
    }
  }
}
html {
  body {
    link(url: url, label: "Google", inline: true)
  }
}
VStack(justify: .center, align: .center) {
  HStack(justify: .spaceEvenly, align: .center) {
    Image(url: "/images/error_bomb.png")
      .width(100)
      .height(100)
    Header(.header3) { "HTTP 500" }
      .font(weight: "bold", size: 40, family: "SF Mono")
  }          
  Paragraph(fiveOfiveMessage)
}
.backgroundColor(GColors.lightRed)
.textAlign(.center)
.margin(5, .percent)
.display(.flex)
.shadow(x: 20, y: 30, color: GColors.cardShadow)
.border(width: 1, color: .black)
struct IndexPage: HTML {
  var body: some HTMLConvertible {
    Root(language: .en) {
      Body {
        Group {
          Text("Welcome to Mongrel!")
            .heading(.h1)

          Text("A Swift and HTML hybrid supporting:")

          List(.unordered) {
            Text("CSS")
              .paragraph()

            Text("Javascript")
              .paragraph()
          }
          .class("list")
        }
      }
      .id("main-body")
    }
  }
}
let titillimFont: StaticString = """
  https://fonts.googleapis.com/css2?family=\
  Titillium+Web:ital,wght@0,200;0,300;0,400;\
  0,600;0,700;0,900;1,200;1,300;1,400;1,600;\
  1,700&display=swap
  """
let page = document {
  html {
    head {
      title("Hello, Swep!")
      style {
        `import`(titillimFont)
        selector("*, *::before, *::after") {
          margin(0)
          padding(0)
        }
        selector("body") {
          margin(0, .auto)
          backgroundColor(.hex(0x111))
          fontFamily("'Titillium Web', sans-serif")
        }
      }
    }
    body {
      h1("📄 swift-web-page (swep)")
      ul {
        li("Write html documents along with css")
          .fontWeight(.bolder)
        li("Bring all swift-language features out of the box")
        #if swift(>=5.1)
          for version in 1...4 {
            if version != 2 {
              li("supporting swift v5.\(version)")
            }
          }
        #endif
      }
      blockquote("Enjoy! ✌️😁")
    }
  }
}

Parsing

struct Group {
    let h1: String
    let h2: String
}

let capture = HTML {
    Capture("#group h1", transform: \.textContent)
    Capture("#group h2", transform: \.textContent)

} transform: { (output: (String, String)) -> Group in
    return Group(
        h1: output.0,
        h2: output.1
    )
} // => HTML<Group>
struct SomeObject: DeepCodable {
    static let codingTree = CodingTree {
        Key("a", "b", "c") {
            Key("d1", "e1", containing: \._e1)
            Key("d2", "e2", "f2", containing: \._f2)
        }
    }

    @Value var e1: String
    @Value var f2: String
}

let json = """
    {
        "a": {
            "b": {
                "c": {
                    "d1": {
                        "e1": "Some value"
                    },
                    "d2": {
                        "e2": {
                            "f2": "Other value"
                        }
                    }
                }
            }
        }
    }
    """
let jsonData = json.data(using: .utf8)!

let object = try JSONDecoder().decode(SomeObject.self, from: jsonData)
print(object.e1) // "Some value"
print(object.f2) // "Other value"

Networking

Request {
  Url("https://jsonplaceholder.typicode.com/todo")
  Header.Accept(.json)
}
.onData { ... }
let myJson = Json {
  JsonProperty(key: "firstName", value: "Carson")
}
myJson["firstName"].string // "Carson"

NSAttributedString

NSAttributedString {
  AText("Hello world")
    .font(.systemFont(ofSize: 24))
    .foregroundColor(.red)
  LineBreak()
  AText("with Swift")
     .font(.systemFont(ofSize: 20))
     .foregroundColor(.orange)
}

REST

var api = Api {
    BasicAuthGroup<User>("login") { login }
    JWTAuthGroup<User.Payload> {
        Group("users") { users }
        Group("inventory") {
            Group("articles") { articles }
        }
    }
}

Server-Side

@main
struct HelloWorld: App {

  var body: some Endpoints {
    Use(logger("dev"), bodyParser.urlencoded())

    Route("/admin") {
      Get("/view") { req, res, _ in res.render("admin-index.html") }
      Render("help", template: "help")
    }

    Get { req, res, next in
      res.render("index.html")
    }
  }
}
struct SampleEndpoint: Responder {
    @QueryParameter("sort_direction") 
    var sortDirection: SortDirection = .ascending

    @URLParameter(\.id) var userID
    @EnivronmentObject var database: Database

    func body() throws {
        JSON(database.fetchFollowers(of: userID, sortDirection: sortDirection))
    }
}

Server(errorRenderer: BasicErrorRenderer()).register {
  SampleEndpoint()
      .on("/api/users/\(\.id))/followers")
}
.environmentObject(Database())
.listen()
Group("api") {
    // GET /api/
    Route(use: index)

    // GET /api/todos
    Route("todos") { req in
        return req.view.render("todos")
    }

    // POST /api/todos
    Route("todos", on: .POST, use: todo)
}

SwiftUI

List(dogs.identified(by: \.name)) { dog in
  SwitchValue(dog) {
    CaseIs(Animal.self) { value in
      Text(value.species)
    }
    CaseIs(Dog.self) { value in
      Text(value.breed)
    }
  }
}
Path {
  Move(to: CGPoint(x: 50, y: 50))
  Line(to: CGPoint(x: 100, y: 100))
  Line(to: CGPoint(x: 0, y: 100))
  Close()
}
VStack {
  Text("🥑🍞 #\(counter)")
    .padding(.all)
    .background(.green, cornerRadius: 12)
    .foregroundColor(.white)
    .tapAction(self.countUp)
}
struct EnumerationView<Content: Sequence>: View where Content.Element: View {

    let content: Content

    init(@SequenceBuilder builder: () -> Content) {
        self.content = builder()
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            ForEach(sequence: content) { (index, content) in
                HStack(alignment: .top) {
                    Text("\(index + 1). ")
                    content
                }
            }
        }
    }
}

// Usage:
EnumerationView {
  Text("Some text")
  VStack {
    ForEach(0..<10, id: \.self) { _ in
      Text("Lorem ipsum dolet.")
    }
  }
  HStack {
    Text("With image:")
    Image(systemName: "checkmark")
  }
}
// Define an Entity:

struct Foo: Entity, Identifiable {
    @Attribute var bar: String = "Untitled"

    var id: some Hashable {
        bar
    }
}

// Define a Schema:

struct MySchema: Schema {
    var entities: Entities {
        Foo.self
        Bar.self
        Baz.self
    }
}
struct AppNavigationTree: NavigationTree {
  let homeViewModel: HomeViewModel
  let detailViewModel: DetailViewModel
  let settingsViewModel: SettingsViewModel

  var builder: some PathBuilder {
    Screen(
      HomeScreen.self,
      content: {
        HomeView(viewModel: homeViewModel)
      },
      nesting: {
        DetailScreen.Builder(viewModel: detailViewModel),
        SettingsScreen.Builder(viewModel: settingsViewModel)
      }
    )
  }
}

Based on AppNavigationTree, the following navigation paths are valid:

  /home
  /home/detail?id=0
  /home/settings

More information on the NavigationTree and how to compose PathBuilders can be found here.

Testing

expect {
  Given("I have a universe without any stars") {
      universe.numberOfStars = 0
  }
  When("I add a couple of stars") {
      universe.numberOfStars = 23
  }
  Then("I can see the stars I have added ✨") {
      XCTAssertEqual(universe.numberOfStars, 23)
  }
}
try validate(user) {
    validate(\.id).isPositive()
    validate(\.email) {
        isNotEmpty()
        isEmail()
    }
}

UIKit

BoxCenter {
  BoxVStack {
    BoxElement { toggleView }
    BoxEmpty()
      .frame(height: 20)
    if flag {
      BoxElement { top }
        .aspectRatio(ratio: CGSize(width: 1, height: 1))
    }
  }
}
Lego {
  ForIn(3...4) { x in
    Section(layout:FlowLayout(col: x)) {
      ForIn(0...(x + 3)) { y in
        ImageItem(value: UIImage(named: "\(y % 2)")!)
      }
    }
  }
}
var content: some Node {
  VerticalStack {
    Repeated(0..<count) {
      Button(action: { self.count += 1 }) {
        BoxedText()
      }
    }
  }
  .cornerRadius(20)
  .animation(.spring)
}
let turtle = Turtle {
  penDown()
  loop(9) {
      left(140)
      forward(30)
      left(-100)
      forward(30)
  }
  penUp()
}
image.constraints { view in
    view.centerXAnchor == container.centerXAnchor
    view.topAnchor == container.topAnchor + 20
    view.widthAnchor == container.widthAnchor -- 20
    view.heightAnchor == view.widthAnchor * 0.6
}
icon.constraints { view in
    view.centerAnchor == iconContainer.centerAnchor
    view.sizeAnchor == CGSize(width: 20, height: 20)
}

AppKit

// later, to replace the menu items with different/updated ones: menu.replaceItems { MenuItem("New Item").onSelect { print("Hello!") } }


* ...

## Other
* [Pappe](https://github.com/frameworklabs/Pappe) - A Proof of concept embedded interpreted synchronous DSL for Swift.

```swift
let m = Module { name in
    activity (name.Wait, [name.ticks]) { val in
        exec { val.i = val.ticks as Int }
        whileRepeat(val.i > 0) {
            exec { val.i -= 1 }
            await { true }
        }
    }
    activity (name.Main, []) { val in
        cobegin {
            strong {
                doRun(name.Wait, [10])
            }
            weak {
                loop {
                    doRun(name.Wait, [2])
                    exec { print("on every third") }
                    await { true }
                }
            }
            weak {
                loop {
                    doRun(name.Wait, [1])
                    exec { print("on every second") }
                    await { true }
                }
            }
        }
        exec { print("done") }
    }
}
import SyntaxBuilder

struct UserSourceFile: SourceFile {
    let idType: Type

    @SyntaxListBuilder
    var body: Body {
        Import("Foundation")

        Struct("User") {
            Typealias("ID", of: idType)

            Let("id", of: "ID")
                .prependingComment("The user's ID.", .docLine)

            Let("name", of: "String")
                .prependingComment("The user's name.", .docLine)

            Var("age", of: "Int")
                .prependingComment("The user's age.", .docLine)

            ForEach(0 ..< 3) { i in
                Let("value\(i)", of: "String")
                    .prependingNewline()
            }
        }
        .prependingComment("""
            User is an user.
            <https://github.com/akkyie/SyntaxBuilder/>
        """, .docBlock)
    }
}
import Narratore

struct MyFirstScene: Scene {
  typealias Game = MyGame

  static let branches: [RawBranch<MyGame>] = [
    Main.raw,
  ]

  enum Main: Branch {
    typealias Anchor = String

    @BranchBuilder<Self>
    static func getSteps(for _: MyFirstScene) -> [BranchStep<Self>] {
      "Welcome"

      "This is your new game, built with narratore".with(tags: [.init("Let's play some sound effect!")])

      check {
        if $0.world.isEnjoyable {
          "Enjoy!"
        }
      }

      "Now choose".with(anchor: "We could jump right here from anywhere")

      choose { _ in
        "Go to second scene, main path".onSelect {
          "Let's go to the second scene!"
            .with(id: "We can keep track of this message")
            .then(.transitionTo(MySecondScene.init(magicNumber: 42)))
        }

        "Go to second scene, alternate path".onSelect {
          "Going to the alternate path of the second scene"
            .then(.transitionTo(MySecondScene.Other.self, scene: .init(magicNumber: 43)))
        }
      }
    }
  }
}