Skip to content
Open
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
5 changes: 5 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,10 @@ let package = Package(
dependencies: ["SyntaxKit", "DocumentationHarness"],
swiftSettings: swiftSettings
),
.testTarget(
name: "AiSTKitTests",
dependencies: ["AiSTKit"],
swiftSettings: swiftSettings
),
]
)
81 changes: 81 additions & 0 deletions Sources/AiSTKit/AuthenticationMiddleware.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// AuthenticationMiddleware.swift
// SyntaxKit
//
// Created by Leo Dion.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//

public import HTTPTypes
public import OpenAPIRuntime

#if os(Linux)
@preconcurrency public import struct Foundation.URL
#else
public import struct Foundation.URL
#endif

/// OpenAPI client middleware that adds Anthropic API authentication headers.
///
/// Injects the `x-api-key` and `anthropic-version` headers into every
/// outgoing request before forwarding it to the next handler in the chain.
public struct AuthenticationMiddleware: ClientMiddleware {
/// The Anthropic API version sent with every request.
private static let anthropicVersion = "2023-06-01"

/// The Anthropic API key used to authenticate requests.
private let apiKey: String

/// Creates a middleware that authenticates requests with the given API key.
/// - Parameter apiKey: The Anthropic API key to send in the `x-api-key` header.
public init(apiKey: String) {
self.apiKey = apiKey
}

/// Adds the Anthropic authentication headers, then forwards the request.
/// - Parameters:
/// - request: The outgoing HTTP request.
/// - body: The outgoing HTTP request body, if any.
/// - baseURL: The base URL of the server.
/// - operationID: The OpenAPI operation identifier for the request.
/// - next: The next handler in the middleware chain.
/// - Returns: The HTTP response and body from the next handler.
/// - Throws: Any error thrown by the `next` handler.
public func intercept(
_ request: HTTPRequest,
body: HTTPBody?,
baseURL: URL,
operationID: String,
next: @Sendable (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
) async throws -> (HTTPResponse, HTTPBody?) {
var request = request
if let name = HTTPField.Name("x-api-key") {
request.headerFields[name] = apiKey
}
if let name = HTTPField.Name("anthropic-version") {
request.headerFields[name] = Self.anthropicVersion
}
return try await next(request, body, baseURL)
}
}
81 changes: 49 additions & 32 deletions Sources/SyntaxKit/Declarations/PoundIf+Condition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,38 +28,6 @@
//

extension PoundIf {
// swiftlint:disable identifier_name

/// Canonical `#if` checks, mirroring Swift's conditional-compilation grammar.
public indirect enum Condition: Sendable {
/// `canImport(<module>)`
case canImport(String)
/// A bare compilation flag such as `DEBUG`.
case flag(String)
/// `os(<OperatingSystem>)`
case os(OperatingSystem)
/// `arch(<Architecture>)`
case arch(Architecture)
/// `targetEnvironment(<TargetEnvironment>)`
case targetEnvironment(TargetEnvironment)
/// `swift(>=5.9)` and friends.
case swift(VersionCheck)
/// `compiler(>=5.9)` and friends.
case compiler(VersionCheck)
/// `hasFeature(<name>)`
case hasFeature(String)
/// `hasAttribute(<name>)`
case hasAttribute(String)
/// `<lhs> && <rhs>`
case and(Condition, Condition)
/// `<lhs> || <rhs>`
case or(Condition, Condition)
/// `!<operand>`
case not(Condition)
}

// swiftlint:enable identifier_name

/// Operating-system identifiers used in `os(...)` checks.
public enum OperatingSystem: String, Sendable {
/// `os(iOS)`
Expand Down Expand Up @@ -160,3 +128,52 @@ extension PoundIf {
}
}
}

extension PoundIf {
/// A canonical `#if` check that can render itself as conditional-compilation
/// syntax, mirroring Swift's grammar.
public protocol Condition: Sendable {
/// Render this condition as `#if` source text.
/// - Parameter atTopLevel: When `true`, a binary combinator omits its
/// surrounding parentheses (it is the outermost expression).
/// - Returns: The rendered conditional-compilation expression.
func render(atTopLevel: Bool) -> String
}

/// A single check rendered as `keyword(argument)`, or a bare `argument` when
/// ``LeafCondition/keyword`` is `nil` (a flag such as `DEBUG`).
public protocol LeafCondition: Condition {
/// The leading keyword, e.g. `os`, or `nil` for a bare flag.
var keyword: String? { get }
/// The text placed inside the parentheses, or the bare flag identifier.
var argument: String { get }
}

/// A `lhs <symbol> rhs` combinator, parenthesized unless at the top level.
public protocol BinaryCondition: Condition {
/// The infix operator rendered between the operands, e.g. `&&`.
var symbol: String { get }
/// The left-hand operand.
var lhs: any Condition { get }
/// The right-hand operand.
var rhs: any Condition { get }
}
}

extension PoundIf.LeafCondition {
/// Render the leaf as `keyword(argument)`, or bare `argument` for a flag.
public func render(atTopLevel _: Bool) -> String {
guard let keyword else {
return argument
}
return "\(keyword)(\(argument))"
}
}

extension PoundIf.BinaryCondition {
/// Render as `lhs symbol rhs`, wrapping in parentheses unless at the top level.
public func render(atTopLevel: Bool) -> String {
let inner = "\(lhs.render(atTopLevel: false)) \(symbol) \(rhs.render(atTopLevel: false))"
return atTopLevel ? inner : "(\(inner))"
}
}
159 changes: 159 additions & 0 deletions Sources/SyntaxKit/Declarations/PoundIf+ConditionTypes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//
// PoundIf+ConditionTypes.swift
// SyntaxKit
//
// Created by Leo Dion.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//

extension PoundIf {
/// `canImport(<module>)`
public struct CanImport: LeafCondition {
/// The module name passed to `canImport`.
public let module: String
/// The `canImport` keyword.
public var keyword: String? { "canImport" }
/// The module name rendered inside the parentheses.
public var argument: String { module }
}

/// A bare compilation flag such as `DEBUG`.
public struct Flag: LeafCondition {
/// The flag identifier.
public let name: String
/// A flag has no keyword and renders bare.
public var keyword: String? { nil }
/// The flag identifier rendered as-is.
public var argument: String { name }
}

/// `os(<OperatingSystem>)`
public struct OSCheck: LeafCondition {
/// The operating system being checked.
public let value: OperatingSystem
/// The `os` keyword.
public var keyword: String? { "os" }
/// The operating-system identifier rendered inside the parentheses.
public var argument: String { value.rawValue }
}

/// `arch(<Architecture>)`
public struct ArchCheck: LeafCondition {
/// The CPU architecture being checked.
public let value: Architecture
/// The `arch` keyword.
public var keyword: String? { "arch" }
/// The architecture identifier rendered inside the parentheses.
public var argument: String { value.rawValue }
}

/// `targetEnvironment(<TargetEnvironment>)`
public struct TargetEnvironmentCheck: LeafCondition {
/// The target environment being checked.
public let value: TargetEnvironment
/// The `targetEnvironment` keyword.
public var keyword: String? { "targetEnvironment" }
/// The environment identifier rendered inside the parentheses.
public var argument: String { value.rawValue }
}

/// `swift(>=5.9)` and friends.
public struct SwiftCheck: LeafCondition {
/// The Swift language version check.
public let check: VersionCheck
/// The `swift` keyword.
public var keyword: String? { "swift" }
/// The version comparison rendered inside the parentheses.
public var argument: String { check.rendered }
}

/// `compiler(>=5.9)` and friends.
public struct CompilerCheck: LeafCondition {
/// The compiler version check.
public let check: VersionCheck
/// The `compiler` keyword.
public var keyword: String? { "compiler" }
/// The version comparison rendered inside the parentheses.
public var argument: String { check.rendered }
}

/// `hasFeature(<name>)`
public struct HasFeature: LeafCondition {
/// The upcoming/experimental feature name.
public let name: String
/// The `hasFeature` keyword.
public var keyword: String? { "hasFeature" }
/// The feature name rendered inside the parentheses.
public var argument: String { name }
}

/// `hasAttribute(<name>)`
public struct HasAttribute: LeafCondition {
/// The attribute name.
public let name: String
/// The `hasAttribute` keyword.
public var keyword: String? { "hasAttribute" }
/// The attribute name rendered inside the parentheses.
public var argument: String { name }
}
}

// MARK: - Combinators

extension PoundIf {
// swiftlint:disable type_name

/// `<lhs> && <rhs>`
public struct And: BinaryCondition {
/// The left-hand operand.
public let lhs: any Condition
/// The right-hand operand.
public let rhs: any Condition
/// The `&&` operator.
public var symbol: String { "&&" }
}

/// `<lhs> || <rhs>`
public struct Or: BinaryCondition {
/// The left-hand operand.
public let lhs: any Condition
/// The right-hand operand.
public let rhs: any Condition
/// The `||` operator.
public var symbol: String { "||" }
}

// swiftlint:enable type_name

/// `!<operand>`
public struct Not: Condition {
/// The condition being negated.
public let operand: any Condition

/// Render as `!operand`, with the operand parenthesized as needed.
public func render(atTopLevel _: Bool) -> String {
"!\(operand.render(atTopLevel: false))"
}
}
}
41 changes: 1 addition & 40 deletions Sources/SyntaxKit/Declarations/PoundIf+Rendering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ extension PoundIf {
private static func renderCondition(_ form: ConditionForm) -> ExprSyntax? {
switch form {
case .helper(let condition):
return parseExpression(renderHelper(condition, atTopLevel: true))
return parseExpression(condition.render(atTopLevel: true))
case .raw(let text):
return parseExpression(text)
case .codeBlock(let block):
Expand All @@ -77,43 +77,4 @@ extension PoundIf {
}
return nil
}

private static func renderHelper(_ condition: Condition, atTopLevel: Bool) -> String {
if let leaf = renderLeaf(condition) {
return leaf
}
return renderCombinator(condition, atTopLevel: atTopLevel)
}

private static func renderLeaf(_ condition: Condition) -> String? {
switch condition {
case .canImport(let module): return "canImport(\(module))"
case .flag(let name): return name
case .os(let value): return "os(\(value.rawValue))"
case .arch(let value): return "arch(\(value.rawValue))"
case .targetEnvironment(let value): return "targetEnvironment(\(value.rawValue))"
case .swift(let check): return "swift(\(check.rendered))"
case .compiler(let check): return "compiler(\(check.rendered))"
case .hasFeature(let name): return "hasFeature(\(name))"
case .hasAttribute(let name): return "hasAttribute(\(name))"
case .and, .or, .not: return nil
}
}

private static func renderCombinator(_ condition: Condition, atTopLevel: Bool) -> String {
switch condition {
case .and(let lhs, let rhs):
let inner =
"\(renderHelper(lhs, atTopLevel: false)) && \(renderHelper(rhs, atTopLevel: false))"
return atTopLevel ? inner : "(\(inner))"
case .or(let lhs, let rhs):
let inner =
"\(renderHelper(lhs, atTopLevel: false)) || \(renderHelper(rhs, atTopLevel: false))"
return atTopLevel ? inner : "(\(inner))"
case .not(let operand):
return "!\(renderHelper(operand, atTopLevel: false))"
default:
return ""
}
}
}
Loading