Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
DerivedData
Package.resolved
.swiftpm

.index-build
70 changes: 70 additions & 0 deletions .swift-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"fileScopedDeclarationPrivacy": {
"accessLevel": "private"
},
"indentation": {
"spaces": 4
},
"indentConditionalCompilationBlocks": false,
"indentSwitchCaseLabels": false,
"lineBreakAroundMultilineExpressionChainComponents": false,
"lineBreakBeforeControlFlowKeywords": false,
"lineBreakBeforeEachArgument": false,
"lineBreakBeforeEachGenericRequirement": false,
"lineLength": 140,
"maximumBlankLines": 1,
"multiElementCollectionTrailingCommas": true,
"noAssignmentInExpressions": {
"allowedFunctions": [
"XCTAssertNoThrow"
]
},
"prioritizeKeepingFunctionOutputTogether": false,
"respectsExistingLineBreaks": true,
"rules": {
"AllPublicDeclarationsHaveDocumentation": false,
"AlwaysUseLiteralForEmptyCollectionInit": false,
"AlwaysUseLowerCamelCase": true,
"AmbiguousTrailingClosureOverload": true,
"BeginDocumentationCommentWithOneLineSummary": false,
"DoNotUseSemicolons": true,
"DontRepeatTypeInStaticProperties": true,
"FileScopedDeclarationPrivacy": true,
"FullyIndirectEnum": true,
"GroupNumericLiterals": true,
"IdentifiersMustBeASCII": true,
"NeverForceUnwrap": false,
"NeverUseForceTry": false,
"NeverUseImplicitlyUnwrappedOptionals": false,
"NoAccessLevelOnExtensionDeclaration": true,
"NoAssignmentInExpressions": true,
"NoBlockComments": true,
"NoCasesWithOnlyFallthrough": true,
"NoEmptyTrailingClosureParentheses": true,
"NoLabelsInCasePatterns": true,
"NoLeadingUnderscores": false,
"NoParensAroundConditions": true,
"NoPlaygroundLiterals": true,
"NoVoidReturnOnFunctionSignature": true,
"OmitExplicitReturns": false,
"OneCasePerLine": true,
"OneVariableDeclarationPerLine": true,
"OnlyOneTrailingClosureArgument": true,
"OrderedImports": true,
"ReplaceForEachWithForLoop": true,
"ReturnVoidInsteadOfEmptyTuple": true,
"TypeNamesShouldBeCapitalized": true,
"UseEarlyExits": false,
"UseExplicitNilCheckInConditions": true,
"UseLetInEveryBoundCaseVariable": true,
"UseShorthandTypeNames": true,
"UseSingleLinePropertyGetter": true,
"UseSynthesizedInitializer": true,
"UseTripleSlashForDocumentationComments": true,
"UseWhereClausesInForLoops": false,
"ValidateDocumentationComments": false
},
"spacesAroundRangeFormationOperators": false,
"tabWidth": 4,
"version": 1
}
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6.1.0
20 changes: 9 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.10
// swift-tools-version:6.1
import PackageDescription

let package = Package(
Expand All @@ -13,7 +13,7 @@ let package = Package(
.library(name: "LeafKit", targets: ["LeafKit"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.81.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.81.0")
],
targets: [
.target(
Expand All @@ -28,20 +28,18 @@ let package = Package(
.testTarget(
name: "LeafKitTests",
dependencies: [
.target(name: "LeafKit"),
.target(name: "LeafKit")
],
exclude: [
"Templates",
"Templates"
],
swiftSettings: swiftSettings
),
]
)

var swiftSettings: [SwiftSetting] { [
.enableUpcomingFeature("ExistentialAny"),
.enableUpcomingFeature("ConciseMagicFile"),
.enableUpcomingFeature("ForwardTrailingClosures"),
.enableUpcomingFeature("DisableOutwardActorInference"),
.enableExperimentalFeature("StrictConcurrency=complete"),
] }
var swiftSettings: [SwiftSetting] {
[
.enableUpcomingFeature("ExistentialAny")
]
}
9 changes: 5 additions & 4 deletions Sources/LeafKit/Character+Identities.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// swift-format-ignore
/// Various helper identities for convenience
extension Character {

Expand All @@ -6,7 +7,7 @@ extension Character {
var isValidInTagName: Bool {
self.isLowercaseLetter || self.isUppercaseLetter
}

var isValidInParameter: Bool {
self.isValidInTagName ||
self.isValidOperator ||
Expand Down Expand Up @@ -45,9 +46,9 @@ extension Character {
false
}
}

// MARK: - General group-membership identities (Internal)

var isHexadecimal: Bool {
(.zero ... .nine).contains(self) ||
(.A ... .F).contains(self) ||
Expand All @@ -70,7 +71,7 @@ extension Character {
var isLowercaseLetter: Bool {
(.a ... .z).contains(self)
}

// MARK: - General helpers

static let newLine: Self = "\n"
Expand Down
10 changes: 5 additions & 5 deletions Sources/LeafKit/LeafAST.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import NIOCore
/// `LeafAST` represents a "compiled," grammatically valid Leaf template (which may or may not be fully resolvable or erroring)
public struct LeafAST: Hashable, Sendable {
// MARK: - Public

public func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
Expand All @@ -23,7 +23,7 @@ public struct LeafAST: Hashable, Sendable {

self.updateRefs([:])
}

init(from: LeafAST, referencing externals: [String: LeafAST]) {
self.name = from.name
self.ast = from.ast
Expand All @@ -39,9 +39,9 @@ public struct LeafAST: Hashable, Sendable {
private(set) var externalRefs = Set<String>()
private(set) var unresolvedRefs = Set<String>()
private(set) var flat: Bool

// MARK: - Private Only

private var rawAST: [Syntax]?

mutating private func updateRefs(_ externals: [String: LeafAST]) {
Expand Down Expand Up @@ -69,7 +69,7 @@ public struct LeafAST: Hashable, Sendable {
pos = self.ast.index(after: pos)
continue
}

// replace the original Syntax with the results of inlining, potentially 1...n
let replacementSyntax = self.ast[pos].inlineRefs(providedExts, [:])
self.ast.replaceSubrange(pos...pos, with: replacementSyntax)
Expand Down
94 changes: 36 additions & 58 deletions Sources/LeafKit/LeafCache/DefaultLeafCache.swift
Original file line number Diff line number Diff line change
@@ -1,102 +1,80 @@
import NIOCore
import NIOConcurrencyHelpers
import NIOCore

public final class DefaultLeafCache: SynchronousLeafCache {
public actor DefaultLeafCache: LeafCache {
// MARK: - Public - `LeafCache` Protocol Conformance

/// Global setting for enabling or disabling the cache
public var isEnabled: Bool = true
private(set) public var isEnabled: Bool = true
/// Current count of cached documents
public var count: Int {
self.lock.withLock { self.cache.count }
}
public var count: Int { self.cache.count }

/// Initializer
public init() {
self.lock = .init()
self.cache = [:]
}

public func toggleEnabled() {
self.isEnabled = !isEnabled
}

/// - Parameters:
/// - document: The `LeafAST` to store
/// - loop: `EventLoop` to return futures on
/// - replace: If a document with the same name is already cached, whether to replace or not.
/// - Returns: The document provided as an identity return
public func insert(
_ document: LeafAST,
on loop: any EventLoop,
replace: Bool = false
) -> EventLoopFuture<LeafAST> {
public func insert(_ document: LeafAST, replace: Bool = false) async throws -> LeafAST {
// future fails if caching is enabled
guard self.isEnabled else {
return loop.makeSucceededFuture(document)
return document
}

return self.lock.withLock {
// return an error if replace is false and the document name is already in cache
switch (self.cache.keys.contains(document.name), replace) {
case (true, false):
return loop.makeFailedFuture(LeafError(.keyExists(document.name)))
default:
self.cache[document.name] = document
}
return loop.makeSucceededFuture(document)
// return an error if replace is false and the document name is already in cache
switch (self.cache.keys.contains(document.name), replace) {
case (true, false):
throw LeafError.keyExists(document.name)
default:
self.cache[document.name] = document
}
return document
}

/// - Parameters:
/// - documentName: Name of the `LeafAST` to try to return
/// - loop: `EventLoop` to return futures on
/// - Returns: `EventLoopFuture<LeafAST?>` holding the `LeafAST` or nil if no matching result
public func retrieve(
documentName: String,
on loop: any EventLoop
) -> EventLoopFuture<LeafAST?> {
/// - Returns: ``LeafAST` or nil if no matching result
public func retrieve(documentName: String) async throws -> LeafAST? {
guard self.isEnabled else {
return loop.makeSucceededFuture(nil)
}
return self.lock.withLock {
loop.makeSucceededFuture(self.cache[documentName])
return nil
}
return self.cache[documentName]
}

/// - Parameters:
/// - documentName: Name of the `LeafAST` to try to purge from the cache
/// - loop: `EventLoop` to return futures on
/// - Returns: `EventLoopFuture<Bool?>` - If no document exists, returns nil. If removed,
/// - Returns: If no document exists, returns nil. If removed,
/// returns true. If cache can't remove because of dependencies (not yet possible), returns false.
public func remove(
_ documentName: String,
on loop: any EventLoop
) -> EventLoopFuture<Bool?> {
public func remove(_ documentName: String) async throws -> Bool? {
guard self.isEnabled else {
return loop.makeFailedFuture(LeafError(.cachingDisabled))
throw LeafError.cachingDisabled()
}

return self.lock.withLock {
guard self.cache[documentName] != nil else {
return loop.makeSucceededFuture(nil)
}
self.cache[documentName] = nil
return loop.makeSucceededFuture(true)
guard self.cache[documentName] != nil else {
return nil
}
self.cache[documentName] = nil
return true
}

// MARK: - Internal Only

let lock: NIOLock
var cache: [String: LeafAST]

/// Blocking file load behavior
func retrieve(documentName: String) throws -> LeafAST? {
guard self.isEnabled else {
throw LeafError(.cachingDisabled)
throw LeafError.cachingDisabled()
}
return try self.lock.withLock {
guard let result = self.cache[documentName] else {
throw LeafError(.noValueForKey(documentName))
}
return result
guard let result = self.cache[documentName] else {
throw LeafError.noValueForKey(documentName)
}
return result
}
}
Loading