Skip to content

Commit

Permalink
Adds StringBuilder implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mattcox committed Aug 13, 2024
1 parent 5b026ad commit 1ac9ae4
Show file tree
Hide file tree
Showing 5 changed files with 418 additions and 1 deletion.
33 changes: 33 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// swift-tools-version: 6.0

import PackageDescription

let package = Package(
name: "StringBuilder",
platforms: [
.macOS(.v10_15),
.iOS(.v13),
.tvOS(.v13),
.watchOS(.v6),
.macCatalyst(.v13)
],
products: [
.library(
name: "StringBuilder",
targets: [
"StringBuilder"
]
),
],
targets: [
.target(
name: "StringBuilder"
),
.testTarget(
name: "StringBuilderTests",
dependencies: [
"StringBuilder"
]
),
]
)
129 changes: 128 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,129 @@
# StringBuilder
Build strings with resultBuilders

<p align="center">
<img src="https://img.shields.io/badge/Swift-orange.svg" alt="Swift" />
<a href="https://swift.org/package-manager">
<img src="https://img.shields.io/badge/swiftpm-compatible-brightgreen.svg?style=flat" alt="Swift Package Manager" />
</a>
</p>

Welcome to **StringBuilder**, a Swift package for dynamically building strings
using a `resultBuilder`.

## Usage

### Basic String Building
To build a basic multi-line string:
```swift
String {
"Line 1"
"Line 2"
}
```

Becomes...
```swift
"""
Line 1
Line 2
"""
```

### Custom Separator
A custom separator can be used to control how the strings are joined:
```swift
String(separator: " != ") {
"String 1"
"String 2"
}
```

Becomes...
```swift
"String 1 != String 2"
```

### String Convertible Types
Any type that conforms to `CustomStringConvertible` can be added to the
string builder:
```swift
String {
1234
true
UUID()
}
```

Becomes...
```swift
1234
true
3D5E76E1-CB1D-4FEF-A5CA-3C67AA08BF47
```

### Nested String Builders
String builders can be nested with different separators at each level:
```swift
String {
"One"

String(separator: ".") {
"Two"
"Three"
}

"Four"

String {
String {
"Five"

String(separator: "\n\n") {
"Six"
"Seven"
}

"Eight"
}

"Nine"
}

"Ten"
}
```

Becomes...
```swift
One
Two.Three
Four
Five
Six

Seven
Eight
Nine
Ten
```

## Installation

StringBuilder is distributed using the [Swift Package Manager](https://swift.org/package-manager). To install it within another Swift package, add it as a dependency within your `Package.swift` manifest:

```swift
let package = Package(
// . . .
dependencies: [
.package(url: "https://github.com/mattcox/StringBuilder.git", branch: "main")
],
// . . .
)
```

If you’d like to use StringBuilder within an iOS, macOS, watchOS or tvOS app, then use Xcode’s `File > Add Packages...` menu command to add it to your project.

Import `StringBuilder` wherever you’d like to use it:
```swift
import StringBuilder
```
40 changes: 40 additions & 0 deletions Sources/StringBuilder/String+StringBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// String+StringBuilder.swift
// StringBuilder
//
// Created by Matt Cox on 13/08/2024.
// Copyright © 2024 Matt Cox. All rights reserved.
//

extension String {
/// Initialize a String from multiple Strings or string convertible types.
///
/// A result builder allows the string to be composed from multiple types,
/// and a separator can be specified to control how the strings are joined
/// together.
///
/// - Parameters:
/// - separator: The separator to insert between each string. This
/// defaults to a new line.
/// - contents: A result builder that will be called to populate the
/// string.
///
/// **Example**
/// ```swift
/// String(separator: "\n\n") {
/// "String"
/// 1234
/// true
/// }
///
/// // String
/// //
/// // 1234
/// //
/// // true
/// ```
///
public init(separator: String = "\n", @StringBuilder _ contents: () -> [String]) {
self = contents().joined(separator: separator)
}
}
72 changes: 72 additions & 0 deletions Sources/StringBuilder/StringBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// StringBuilder.swift
// StringBuilder
//
// Created by Matt Cox on 13/08/2024.
// Copyright © 2024 Matt Cox. All rights reserved.
//

@resultBuilder
public enum StringBuilder {
public static func buildBlock() -> String {
""
}
}

public extension StringBuilder {
static func buildPartialBlock(first: CustomStringConvertible) -> [String] {
[first.description]
}

static func buildPartialBlock(accumulated: [String], next: CustomStringConvertible) -> [String] {
accumulated + [next.description]
}

static func buildPartialBlock(first: String) -> [String] {
[first]
}

static func buildPartialBlock(accumulated: [String], next: String) -> [String] {
accumulated + [next]
}

static func buildPartialBlock(first: [String]) -> [String] {
first
}

static func buildPartialBlock(accumulated: [String], next: [String]) -> [String] {
accumulated + next
}
}

public extension StringBuilder {
static func buildOptional(_ component: String?) -> [String] {
if let component {
return [component]
}

return []
}

static func buildOptional(_ component: [String]?) -> [String] {
component ?? []
}
}

public extension StringBuilder {
static func buildEither(first component: [String]) -> [String] {
component
}

static func buildEither(second component: [String]) -> [String] {
component
}
}

public extension StringBuilder {
static func buildArray(_ components: [[String]]) -> [String] {
components.flatMap {
$0
}
}
}
Loading

0 comments on commit 1ac9ae4

Please # to comment.