diff --git a/Sources/HTMLKit/Framework/Environment/Condition.swift b/Sources/HTMLKit/Framework/Environment/Condition.swift
index 5348438e..50c969e9 100644
--- a/Sources/HTMLKit/Framework/Environment/Condition.swift
+++ b/Sources/HTMLKit/Framework/Environment/Condition.swift
@@ -1,38 +1,38 @@
-/// A type representing a conditional that compares an environment value against another value
+/// A type representing a conditional that compares an environment value against another value.
@_documentation(visibility: internal)
public struct Condition {
/// An enumeration of potential comparison.
public enum Comparison {
- /// Indicates an equal comparison
+ /// Indicates an equal comparison.
case equal
- /// Indicates a not-equal comparison
+ /// Indicates a not-equal comparison.
case unequal
- /// Indicates a greater-than comparison
+ /// Indicates a greater-than comparison.
case greater
- /// Indicates a less-than comparison
+ /// Indicates a less-than comparison.
case less
}
- /// The left-hand side value
+ /// The left-hand side value.
internal let lhs: EnvironmentValue
- /// The right-hand side value to test against
+ /// The right-hand side value to test against.
internal let rhs: any Comparable
- /// The comparison to perfom
+ /// The comparison to perfom.
internal let comparison: Comparison
- /// Initializes a condition
+ /// Creates a condition.
///
/// - Parameters:
- /// - lhs: The origin value
- /// - rhs: The value to atest against
- /// - operation: The comparison to perfom
+ /// - lhs: The origin value.
+ /// - rhs: The value to atest against.
+ /// - operation: The comparison to perfom.
public init(lhs: EnvironmentValue, rhs: any Comparable, comparison: Comparison) {
self.lhs = lhs
diff --git a/Sources/HTMLKit/Framework/Environment/Conditional.swift b/Sources/HTMLKit/Framework/Environment/Conditional.swift
index d8904f7b..8162e5e9 100644
--- a/Sources/HTMLKit/Framework/Environment/Conditional.swift
+++ b/Sources/HTMLKit/Framework/Environment/Conditional.swift
@@ -2,18 +2,18 @@
@_documentation(visibility: internal)
public indirect enum Conditional {
- /// Holds an optional
+ /// Holds an optional.
case optional(EnvironmentValue)
- /// Holds an relation
+ /// Holds a relation.
case relation(Relation)
- /// Holds a condition
+ /// Holds a condition.
case condition(Condition)
- /// Holds a negation
+ /// Holds a negation.
case negation(Negation)
- /// Holds a value
+ /// Holds a value.
case value(EnvironmentValue)
}
diff --git a/Sources/HTMLKit/Framework/Environment/Environment.swift b/Sources/HTMLKit/Framework/Environment/Environment.swift
index 0dad775d..b5eb73c4 100644
--- a/Sources/HTMLKit/Framework/Environment/Environment.swift
+++ b/Sources/HTMLKit/Framework/Environment/Environment.swift
@@ -1,9 +1,9 @@
import Foundation
import NIOConcurrencyHelpers
-/// A class that represents the environment
+/// A type that represents the environment.
///
-/// The environment provides storage for various settings used by the renderer
+/// The environment provides storage for various settings used by the renderer.
@_documentation(visibility: internal)
public final class Environment: @unchecked Sendable {
@@ -54,7 +54,16 @@ public final class Environment: @unchecked Sendable {
self.lock = .init()
}
- /// The current time zone of the environment
+ /// Creates an environment.
+ ///
+ /// - Parameter environment: The environment to derive from.
+ public init(from environment: Environment) {
+
+ self.storage = environment.storage
+ self.lock = .init()
+ }
+
+ /// The current time zone of the environment.
public var timeZone: TimeZone? {
get {
@@ -62,7 +71,7 @@ public final class Environment: @unchecked Sendable {
}
}
- /// The current calendar of the environment
+ /// The current calendar of the environment.
public var calendar: Calendar? {
get {
@@ -70,7 +79,7 @@ public final class Environment: @unchecked Sendable {
}
}
- /// The current locale of the environment
+ /// The current locale of the environment.
public var locale: Locale? {
get {
@@ -78,7 +87,7 @@ public final class Environment: @unchecked Sendable {
}
}
- /// The current color scheme of the environment
+ /// The current color scheme of the environment.
public var colorScheme: String? {
get {
@@ -86,11 +95,11 @@ public final class Environment: @unchecked Sendable {
}
}
- /// Retrieves a value from environment for a given key path
+ /// Retrieves a value from environment for a given key path.
///
- /// - Parameter path: The key path used to look up the value
+ /// - Parameter path: The key path used to look up the value.
///
- /// - Returns: The value
+ /// - Returns: The value.
public func retrieve(for path: AnyKeyPath) -> Any? {
return lock.withLock {
@@ -98,11 +107,11 @@ public final class Environment: @unchecked Sendable {
}
}
- /// Inserts or updates a value in the environment for the given key path
+ /// Inserts or updates a value in the environment for the given key path.
///
/// - Parameters:
- /// - value: The value to be stored or updated
- /// - path: The key path that identifies where the value is stored
+ /// - value: The value to be stored or updated.
+ /// - path: The key path that identifies where the value is stored.
public func upsert(_ value: T, for path: AnyKeyPath) {
lock.withLock {
@@ -110,11 +119,11 @@ public final class Environment: @unchecked Sendable {
}
}
- /// Resolves an environment value
+ /// Resolves an environment value.
///
- /// - Parameter value: The environment value to resolve
+ /// - Parameter value: The environment value to resolve.
///
- /// - Returns: The resolved environment value
+ /// - Returns: The resolved environment value.
internal func resolve(value: EnvironmentValue) throws -> Any {
guard let parent = retrieve(for: value.parentPath) else {
@@ -128,11 +137,11 @@ public final class Environment: @unchecked Sendable {
return value
}
- /// Evaluates an environment value
+ /// Evaluates an environment value.
///
- /// - Parameter value: The value to evaluate
+ /// - Parameter value: The value to evaluate.
///
- /// - Returns: The result of evaluation
+ /// - Returns: The result of evaluation.
internal func evaluate(value: EnvironmentValue) throws -> Bool {
guard let boolValue = try resolve(value: value) as? Bool else {
@@ -142,11 +151,11 @@ public final class Environment: @unchecked Sendable {
return boolValue
}
- /// Evaluates an environment relation
+ /// Evaluates an environment relation.
///
- /// - Parameter relation: The relation to evaluate
+ /// - Parameter relation: The relation to evaluate.
///
- /// - Returns: The result of the evaluation
+ /// - Returns: The result of the evaluation.
internal func evaluate(relation: Relation) throws -> Bool {
switch relation.term {
@@ -242,11 +251,11 @@ public final class Environment: @unchecked Sendable {
}
}
- /// Evaluates an environment condition
+ /// Evaluates an environment condition.
///
- /// - Parameter condition: The condition to evaluate
+ /// - Parameter condition: The condition to evaluate.
///
- /// - Returns: The result of the evaluation
+ /// - Returns: The result of the evaluation.
internal func evaluate(condition: Condition) throws -> Bool {
let value = try resolve(value: condition.lhs)
@@ -266,20 +275,20 @@ public final class Environment: @unchecked Sendable {
}
}
- /// Evaluates an environment negation
+ /// Evaluates an environment negation.
///
- /// - Parameter negation: The negation to evaluate
+ /// - Parameter negation: The negation to evaluate.
///
- /// - Returns: The result of the evaluation
+ /// - Returns: The result of the evaluation.
internal func evaluate(negation: Negation) throws -> Bool {
return try !evaluate(value: negation.value)
}
- /// Evaluates an environment optional
+ /// Evaluates an environment optional.
///
- /// - Parameter optional: The optional to evaluate
+ /// - Parameter optional: The optional to evaluate.
///
- /// - Returns: The result of the evaluation
+ /// - Returns: The result of the evaluation.
internal func evaluate(optional: EnvironmentValue) throws -> Bool {
guard let optionalValue = try resolve(value: optional) as? Nullable else {
@@ -296,82 +305,82 @@ public final class Environment: @unchecked Sendable {
extension Environment {
- /// Evaluates one condition
+ /// Evaluates one condition.
///
/// - Parameters:
- /// - condition: The condition to evaluate
- /// - content: The content for the true statement
+ /// - condition: The condition to evaluate.
+ /// - content: The content for the true statement.
///
- /// - Returns: A environment statement
+ /// - Returns: An environment statement.
public static func check(_ condition: EnvironmentValue, @ContentBuilder content: () -> [Content]) -> Statement {
return Statement(compound: .value(condition), first: content(), second: [])
}
- /// Evaluates one condition
+ /// Evaluates one condition.
///
/// - Parameters:
- /// - condition: The condition to evaluate
- /// - content: The content for the true statement
- /// - then: The content for the false statement
+ /// - condition: The condition to evaluate.
+ /// - content: The content for the true statement.
+ /// - then: The content for the false statement.
///
- /// - Returns: A environment statement
+ /// - Returns: An environment statement.
public static func check(_ condition: EnvironmentValue, @ContentBuilder content: () -> [Content], @ContentBuilder then: () -> [Content]) -> Statement {
return Statement(compound: .value(condition), first: content(), second: then())
}
- /// Evaluates one condition
+ /// Evaluates one condition.
///
/// - Parameters:
- /// - condition: The condition to evaluate
- /// - content: The content for the true statement
+ /// - condition: The condition to evaluate.
+ /// - content: The content for the true statement.
///
- /// - Returns: A environment statement
+ /// - Returns: An environment statement.
public static func check(_ condition: Conditional, @ContentBuilder content: () -> [Content]) -> Statement {
return Statement(compound: condition, first: content(), second: [])
}
- /// Evaluates one condition
+ /// Evaluates one condition.
///
/// - Parameters:
- /// - condition: The condition to evaluate
- /// - content: The content for the true statement
- /// - then: The content for the false statement
+ /// - condition: The condition to evaluate.
+ /// - content: The content for the true statement.
+ /// - then: The content for the false statement.
///
- /// - Returns: A environment statement
+ /// - Returns: An environment statement.
public static func check(_ condition: Conditional, @ContentBuilder content: () -> [Content], @ContentBuilder then: () -> [Content]) -> Statement {
return Statement(compound: condition, first: content(), second: then())
}
- /// Unwraps a optional environment value
+ /// Unwraps an optional environment value.
///
/// - Parameters:
- /// - value: The optional value to unwrap
- /// - content: The content for some statement
+ /// - value: The optional value to unwrap.
+ /// - content: The content for some statement.
///
- /// - Returns: A environment statement
+ /// - Returns: An environment statement.
public static func unwrap(_ value: EnvironmentValue, @ContentBuilder content: (EnvironmentValue) -> [Content]) -> Statement {
return Statement(compound: .optional(value), first: content(value), second: [])
}
- /// Unwraps a optional environment value
+ /// Unwraps a noptional environment value.
///
/// - Parameters:
- /// - value: The optional value to unwrap
- /// - content: The content for some statement
- /// - then: The content for none statement
+ /// - value: The optional value to unwrap.
+ /// - content: The content for some statement.
+ /// - then: The content for none statement.
///
- /// - Returns: A environment statement
+ /// - Returns: An environment statement.
public static func unwrap(_ value: EnvironmentValue, @ContentBuilder content: (EnvironmentValue) -> [Content], @ContentBuilder then: () -> [Content]) -> Statement {
return Statement(compound: .optional(value), first: content(value), second: then())
}
- /// Iterates through a sequence of values
+ /// Iterates through a sequence of values.
///
/// - Parameters:
- /// - sequence: The sequence to iterate over
- /// - content: The content for the iteration
+ /// - sequence: The sequence to iterate over.
+ /// - content: The content for the iteration.
///
- /// - Returns: A environment sequence
+ /// - Returns: An environment sequence.
public static func loop(_ sequence: EnvironmentValue, @ContentBuilder content: (EnvironmentValue) -> [Content]) -> Sequence {
return Sequence(value: sequence, content: content(sequence))
}
diff --git a/Sources/HTMLKit/Framework/Environment/EnvironmentKeys.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentKeys.swift
index 39c2154a..6a708485 100644
--- a/Sources/HTMLKit/Framework/Environment/EnvironmentKeys.swift
+++ b/Sources/HTMLKit/Framework/Environment/EnvironmentKeys.swift
@@ -1,19 +1,19 @@
import Foundation
-/// A set of predefined environment keys
+/// A set of predefined environment keys.
///
/// The keys are used on the environment modifiers to configure various settings for the environment.
public struct EnvironmentKeys: Hashable {
- /// A key used to configure the environment's calendar
+ /// A key used to configure the environment's calendar.
public var calendar: Calendar
- /// A key used to configure the environment's time zone
+ /// A key used to configure the environment's time zone.
public var timeZone: TimeZone
- /// A key used to configure the environment's locale
+ /// A key used to configure the environment's locale.
public var locale: Locale
- /// A key used to configure the environment's color scheme
+ /// A key used to configure the environment's color scheme.
public var colorScheme: String
}
diff --git a/Sources/HTMLKit/Framework/Environment/EnvironmentModifier.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentModifier.swift
index 46438175..e8fabec7 100644
--- a/Sources/HTMLKit/Framework/Environment/EnvironmentModifier.swift
+++ b/Sources/HTMLKit/Framework/Environment/EnvironmentModifier.swift
@@ -1,24 +1,24 @@
-/// A type that holds the modification details for the environment
+/// A type that holds the modification for the environment.
///
/// The modifier is received by the renderer and applied to the environment.
@_documentation(visibility: internal)
public struct EnvironmentModifier: Content {
- /// The environment key
+ /// The environment key.
internal let key: AnyKeyPath
- /// The environment value
+ /// The environment value.
internal let value: Any?
- /// The sub-content
+ /// The subcontent.
internal let content: [Content]
- /// Initializes an environment modifier
+ /// Creates an environment modifier.
///
/// - Parameters:
- /// - key: The key path of the environment value to be modified
- /// - value: The new value to update the environment value with
- /// - content: The sub-content to be rendered
+ /// - key: The key path of the environment value to be modified.
+ /// - value: The new value to update the environment value with.
+ /// - content: The subcontent to be rendered.
public init(key: AnyKeyPath, value: Any? = nil, content: [Content]) {
self.key = key
diff --git a/Sources/HTMLKit/Framework/Environment/EnvironmentObject.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentObject.swift
index f47aa845..0946bf76 100644
--- a/Sources/HTMLKit/Framework/Environment/EnvironmentObject.swift
+++ b/Sources/HTMLKit/Framework/Environment/EnvironmentObject.swift
@@ -1,48 +1,48 @@
import Foundation
-/// A property wrapper type to provide access to an environment object
+/// A property wrapper type to provide access to an environment object.
///
/// An environment object allows to read shared environment data.
@frozen @propertyWrapper public struct EnvironmentObject {
- /// The wrapped object
+ /// The wrapped object.
public var wrappedValue: Wrapper
- /// Initialiizes the environment object
+ /// Creates an environment object.
public init(_ type: ObjectType.Type) {
self.wrappedValue = .init()
}
- /// A wrapper, that holds the object informationen
+ /// A wrapper, that holds the object informationen.
@dynamicMemberLookup public struct Wrapper {
- /// The path of the parent
+ /// The path of the parent.
internal var parent: AnyKeyPath?
- /// The path of the value
+ /// The path of the value.
internal var path: AnyKeyPath
- /// Initializes the wrapper
+ /// Initializes the wrapper.
public init() {
self.path = \WrapperType.self
}
- /// Initializes the wrapper
+ /// Creates the wrapper.
///
/// - Parameters:
- /// - parent: The path of the parent
- /// - path: The path of the value
+ /// - parent: The path of the parent.
+ /// - path: The path of the value.
internal init(parent: AnyKeyPath, path: AnyKeyPath) {
self.parent = parent
self.path = path
}
- /// Accesses a wrapped value for a given key path dynamically
+ /// Accesses a wrapped value for a given key path dynamically.
///
- /// - Returns: An environment value
+ /// - Returns: An environment value.
public subscript(dynamicMember member: KeyPath) -> EnvironmentValue {
guard let newPath = self.path.appending(path: member) else {
@@ -56,9 +56,9 @@ import Foundation
return .init(parentPath: self.path, valuePath: newPath)
}
- /// Accesses a wrapped model object for a given key path dynamically
+ /// Accesses a wrapped model object for a given key path dynamically.
///
- /// - Returns: An environment value
+ /// - Returns: An environment value.
public subscript(dynamicMember member: KeyPath) -> Wrapper where T: ViewModel {
guard let newPath = self.path.appending(path: member) else {
@@ -70,5 +70,5 @@ import Foundation
}
}
-/// A type, that defines a view model
+/// A type, that defines a view model.
public protocol ViewModel: Encodable {}
diff --git a/Sources/HTMLKit/Framework/Environment/EnvironmentString.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentString.swift
index c80b5840..d97a5739 100644
--- a/Sources/HTMLKit/Framework/Environment/EnvironmentString.swift
+++ b/Sources/HTMLKit/Framework/Environment/EnvironmentString.swift
@@ -1,31 +1,35 @@
-/// A type that represents an environment string
+/// A type that represents an environment string.
@_documentation(visibility: internal)
-public struct EnvironmentString: ExpressibleByStringLiteral, ExpressibleByStringInterpolation, Content {
+public struct EnvironmentString: Content {
internal var values: [Content] = []
+}
+
+extension EnvironmentString: ExpressibleByStringLiteral {
public init(stringLiteral: String) {
self.values.append(stringLiteral)
}
+}
+
+extension EnvironmentString: ExpressibleByStringInterpolation {
- public init(stringInterpolation: StringInterpolation) {
+ public init(stringInterpolation: EnvironmentString) {
self.values.append(contentsOf: stringInterpolation.values)
}
+}
+
+extension EnvironmentString: StringInterpolationProtocol {
+
+ public init(literalCapacity: Int, interpolationCount: Int) {
+ values.reserveCapacity(interpolationCount)
+ }
- public struct StringInterpolation: StringInterpolationProtocol {
-
- internal var values: [Content] = []
+ public mutating func appendLiteral(_ literal: String) {
+ values.append(literal)
+ }
- public init(literalCapacity: Int, interpolationCount: Int) {
- values.reserveCapacity(interpolationCount)
- }
-
- public mutating func appendLiteral(_ literal: String) {
- values.append(literal)
- }
-
- public mutating func appendInterpolation(_ env: EnvironmentValue) {
- values.append(env)
- }
+ public mutating func appendInterpolation(_ value: EnvironmentValue) {
+ values.append(value)
}
}
diff --git a/Sources/HTMLKit/Framework/Environment/EnvironmentValue.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentValue.swift
index 06afbc0f..ffe5840c 100644
--- a/Sources/HTMLKit/Framework/Environment/EnvironmentValue.swift
+++ b/Sources/HTMLKit/Framework/Environment/EnvironmentValue.swift
@@ -1,22 +1,22 @@
import Foundation
-/// A type that serves as a placeholder for an environment value
+/// A type that serves as a placeholder for an environment value.
///
/// The placeholder will be evaluated and resolved by the renderer when needed.
@_documentation(visibility: internal)
public struct EnvironmentValue: Content {
- /// The path of the values parent
+ /// The path of the values parent.
internal let parentPath: AnyKeyPath
- /// The path of the value
+ /// The path of the value.
internal let valuePath: AnyKeyPath
- /// Initializes a environment value
+ /// Creates an environment value.
///
/// - Parameters:
- /// - parentPath: The key path of the parent
- /// - valuePath: The key path of the value
+ /// - parentPath: The key path of the parent.
+ /// - valuePath: The key path of the value.
internal init(parentPath: AnyKeyPath, valuePath: AnyKeyPath) {
self.parentPath = parentPath
@@ -26,35 +26,35 @@ public struct EnvironmentValue: Content {
extension EnvironmentValue {
- /// Concat environment value with environment value
+ /// Concat environment value with environment value.
public static func + (lhs: Content, rhs: Self) -> Content {
return [lhs, rhs]
}
- /// Compare an environment value with another comparable value
+ /// Compare an environment value with another comparable value.
///
- /// Makes an unequal evaluation
+ /// Makes an unequal evaluation.
public static func != (lhs: Self, rhs: some Comparable) -> Conditional {
return .condition(Condition(lhs: lhs, rhs: rhs, comparison: .unequal))
}
- /// Compare an environment value with another comparable value
+ /// Compare an environment value with another comparable value.
///
- /// Makes an equal evaluation
+ /// Makes an equal evaluation.
public static func == (lhs: Self, rhs: some Comparable) -> Conditional {
return .condition(Condition(lhs: lhs, rhs: rhs, comparison: .equal))
}
- /// Compare an environment value with another comparable value
+ /// Compare an environment value with another comparable value.
///
- /// Makes an less than evaluation
+ /// Makes an less than evaluation.
public static func < (lhs: Self, rhs: some Comparable) -> Conditional {
return .condition(Condition(lhs: lhs, rhs: rhs, comparison: .less))
}
- /// Compare an environment value with another comparable value
+ /// Compare an environment value with another comparable value.
///
- /// Makes an greater than evaluation
+ /// Makes an greater than evaluation.
public static func > (lhs: Self, rhs: some Comparable) -> Conditional {
return .condition(Condition(lhs: lhs, rhs: rhs, comparison: .greater))
}
diff --git a/Sources/HTMLKit/Framework/Environment/Negation.swift b/Sources/HTMLKit/Framework/Environment/Negation.swift
index 042cad6f..fd2a5f9f 100644
--- a/Sources/HTMLKit/Framework/Environment/Negation.swift
+++ b/Sources/HTMLKit/Framework/Environment/Negation.swift
@@ -1,20 +1,20 @@
-/// A type thats represents an invert conditional
+/// A type that represents an invert conditional.
@_documentation(visibility: internal)
public struct Negation {
- /// The left-hand side conditional
+ /// The left-hand side conditional.
internal let value: EnvironmentValue
- /// Initializes the negation
+ /// Creates a negation.
///
- /// - Parameter lhs: The conditional to evaluate
- public init(value: EnvironmentValue) {
+ /// - Parameter value: The value to evaluate.
+ internal init(value: EnvironmentValue) {
self.value = value
}
}
-/// Creates a invert conditional
+/// Creates an invert conditional.
///
/// ```swift
/// Environment.when(!value) {
@@ -22,9 +22,9 @@ public struct Negation {
/// ```
///
/// - Parameters:
-/// - lhs: The left-hand side conditional
+/// - lhs: The left-hand side conditional.
///
-/// - Returns: A invert conditional
+/// - Returns: An invert conditional.
public prefix func ! (value: EnvironmentValue) -> Conditional {
return .negation(Negation(value: value))
}
diff --git a/Sources/HTMLKit/Framework/Environment/Nullable.swift b/Sources/HTMLKit/Framework/Environment/Nullable.swift
index 4ef47815..7f6e4c60 100644
--- a/Sources/HTMLKit/Framework/Environment/Nullable.swift
+++ b/Sources/HTMLKit/Framework/Environment/Nullable.swift
@@ -1,4 +1,4 @@
-/// A type that represent a nullable value.
+/// A type that represents a nullable value.
///
/// > Note: This protocol is intended as a temporary workaround.
@_documentation(visibility: internal)
diff --git a/Sources/HTMLKit/Framework/Environment/Relation.swift b/Sources/HTMLKit/Framework/Environment/Relation.swift
index e67b0554..b6394e7b 100644
--- a/Sources/HTMLKit/Framework/Environment/Relation.swift
+++ b/Sources/HTMLKit/Framework/Environment/Relation.swift
@@ -1,36 +1,36 @@
-/// A type representing the logical relation between two conditionals
+/// A type representing the logical relation between two conditionals.
@_documentation(visibility: internal)
public struct Relation {
/// An enumeration of potential logical terms.
public enum Term {
- /// Indicates a conjunction
+ /// Indicates a conjunction.
///
- /// All conditions must be true for the relation to be true
+ /// All conditions must be true for the relation to be true.
case conjunction
- /// Indicates a disjunction
+ /// Indicates a disjunction.
///
- /// One condition must be at least true for the relation to be true
+ /// One condition must be at least true for the relation to be true.
case disjunction
}
- /// The logical term specifying the relation
+ /// The logical term specifying the relation.
internal let term: Term
- /// The left-hand side conditional
+ /// The left-hand side conditional.
internal let lhs: Conditional
- /// The right-hand side conditional
+ /// The right-hand side conditional.
internal let rhs: Conditional
- /// Initializes a relation
+ /// Creates a relation.
///
/// - Parameters:
- /// - term: The term on which the relation acts on
- /// - lhs: The left-hand side conditional
- /// - rhs: The right-hand side conditional to test against
+ /// - term: The term on which the relation acts on.
+ /// - lhs: The left-hand side conditional.
+ /// - rhs: The right-hand side conditional to test against.
public init(term: Term, lhs: Conditional, rhs: Conditional) {
self.term = term
@@ -39,7 +39,7 @@ public struct Relation {
}
}
-/// Creates a conjunctional relation between two conditionals
+/// Creates a conjunctional relation between two conditionals.
///
/// ```swift
/// Environment.when(value > 0 && value) {
@@ -47,15 +47,15 @@ public struct Relation {
/// ```
///
/// - Parameters:
-/// - lhs: The left-hand side conditional
-/// - rhs: An environment value
+/// - lhs: The left-hand side conditional.
+/// - rhs: An environment value.
///
-/// - Returns: A conjunctional relation
+/// - Returns: A conjunctional relation.
public func && (lhs: Conditional, rhs: EnvironmentValue) -> Conditional {
return .relation(Relation(term: .conjunction, lhs: lhs, rhs: .value(rhs)))
}
-/// Creates a conjunctional relation between two conditionals
+/// Creates a conjunctional relation between two conditionals.
///
/// ```swift
/// Environment.when(value && value > 0) {
@@ -63,16 +63,16 @@ public func && (lhs: Conditional, rhs: EnvironmentValue) -> Conditional {
/// ```
///
/// - Parameters:
-/// - lhs: The left-hand side conditional
-/// - rhs: An environment value
+/// - lhs: The left-hand side conditional.
+/// - rhs: An environment value.
///
-/// - Returns: A conjunctional relation
+/// - Returns: A conjunctional relation.
public func && (lhs: EnvironmentValue, rhs: Conditional) -> Conditional {
return .relation(Relation(term: .conjunction, lhs: .value(lhs), rhs: rhs))
}
-/// Creates a conjunctional relation between two conditionals
+/// Creates a conjunctional relation between two conditionals.
///
/// ```swift
/// Environment.when(value > 0 && value < 2) {
@@ -80,15 +80,15 @@ public func && (lhs: EnvironmentValue, rhs: Conditional) -> Conditional {
/// ```
///
/// - Parameters:
-/// - lhs: The left-hand side conditional
-/// - rhs: The right-hand side conditional
+/// - lhs: The left-hand side conditional.
+/// - rhs: The right-hand side conditional.
///
-/// - Returns: A conjunctional relation
+/// - Returns: A conjunctional relation.
public func && (lhs: Conditional, rhs: Conditional) -> Conditional {
return .relation(Relation(term: .conjunction, lhs: lhs, rhs: rhs))
}
-/// Creates a disjunctional relation between two conditionals
+/// Creates a disjunctional relation between two conditionals.
///
/// ```swift
/// Environment.when(value > 0 || value) {
@@ -96,15 +96,15 @@ public func && (lhs: Conditional, rhs: Conditional) -> Conditional {
/// ```
///
/// - Parameters:
-/// - lhs: The left-hand side conditional
-/// - rhs: The right-hand side conditional
+/// - lhs: The left-hand side conditional.
+/// - rhs: The right-hand side conditional.
///
-/// - Returns: A disjunctional relation
+/// - Returns: A disjunctional relation.
public func || (lhs: Conditional, rhs: EnvironmentValue) -> Conditional {
return .relation(Relation(term: .disjunction, lhs: lhs, rhs: .value(rhs)))
}
-/// Creates a disjunctional relation between two conditionals
+/// Creates a disjunctional relation between two conditionals.
///
/// ```swift
/// Environment.when(value || value > 0) {
@@ -112,15 +112,15 @@ public func || (lhs: Conditional, rhs: EnvironmentValue) -> Conditional {
/// ```
///
/// - Parameters:
-/// - lhs: The left-hand side conditional
-/// - rhs: The right-hand side conditional
+/// - lhs: The left-hand side conditional.
+/// - rhs: The right-hand side conditional.
///
-/// - Returns: A disjunctional relation
+/// - Returns: A disjunctional relation.
public func || (lhs: EnvironmentValue, rhs: Conditional) -> Conditional {
return .relation(Relation(term: .disjunction, lhs: .value(lhs), rhs: rhs))
}
-/// Creates a disjunctional relation between two conditionals
+/// Creates a disjunctional relation between two conditionals.
///
/// ```swift
/// Environment.when(value > 0 || value < 2) {
@@ -128,10 +128,10 @@ public func || (lhs: EnvironmentValue, rhs: Conditional) -> Conditional {
/// ```
///
/// - Parameters:
-/// - lhs: The left-hand side conditional
-/// - rhs: The right-hand side conditional
+/// - lhs: The left-hand side conditional.
+/// - rhs: The right-hand side conditional.
///
-/// - Returns: A disjunctional relation
+/// - Returns: A disjunctional relation.
public func || (lhs: Conditional, rhs: Conditional) -> Conditional {
return .relation(Relation(term: .disjunction, lhs: lhs, rhs: rhs))
}
diff --git a/Sources/HTMLKit/Framework/Environment/Sequence.swift b/Sources/HTMLKit/Framework/Environment/Sequence.swift
index f591ec81..3c66bb94 100644
--- a/Sources/HTMLKit/Framework/Environment/Sequence.swift
+++ b/Sources/HTMLKit/Framework/Environment/Sequence.swift
@@ -1,18 +1,18 @@
-/// A type representing a loop rendering content within the environment
+/// A type representing a loop rendering content within the environment.
@_documentation(visibility: internal)
public struct Sequence: GlobalElement {
- /// The environment value of the sequence
+ /// The environment value of the sequence.
internal let value: EnvironmentValue
- /// The accumulated content
+ /// The accumulated content.
internal let content: [Content]
- /// Initializes a loop
+ /// Creates a sequence.
///
/// - Parameters:
- /// - value: The environment value to retrieve the sequence from
- /// - content: The content to render for each item in the sequence
+ /// - value: The environment value to retrieve the sequence from.
+ /// - content: The content to render for each item in the sequence.
public init(value: EnvironmentValue, content: [Content]) {
self.value = value
diff --git a/Sources/HTMLKit/Framework/Environment/Statement.swift b/Sources/HTMLKit/Framework/Environment/Statement.swift
index 2db7286e..1c1f9dc6 100644
--- a/Sources/HTMLKit/Framework/Environment/Statement.swift
+++ b/Sources/HTMLKit/Framework/Environment/Statement.swift
@@ -1,22 +1,22 @@
-/// A type representing a conditional block within the environment
+/// A type representing a conditional block within the environment.
@_documentation(visibility: internal)
public struct Statement: GlobalElement {
- /// The compound condition
+ /// The compound condition.
internal let compound: Conditional
- /// The first statement
+ /// The first statement.
internal let first: [Content]
- /// The second statement
+ /// The second statement.
internal let second: [Content]
- /// Initializes a statement
+ /// Creates a statement.
///
/// - Parameters:
- /// - compound: The compound of conditionals
- /// - first: The statement to execute if conditionals are true
- /// - second: The statement to execute if conditionals are false
+ /// - compound: The compound of conditionals.
+ /// - first: The statement to execute if conditionals are true.
+ /// - second: The statement to execute if conditionals are false.
public init(compound: Conditional, first: [Content], second: [Content]) {
self.compound = compound
diff --git a/Sources/HTMLKit/Framework/Rendering/Renderer.swift b/Sources/HTMLKit/Framework/Rendering/Renderer.swift
index 0354e2de..8873f64e 100644
--- a/Sources/HTMLKit/Framework/Rendering/Renderer.swift
+++ b/Sources/HTMLKit/Framework/Rendering/Renderer.swift
@@ -353,7 +353,7 @@ public struct Renderer: Sendable {
}
}
- /// Renders a environment modifier
+ /// Renders an environment modifier
///
/// - Parameter modifier: The modifier to apply to
///
@@ -367,7 +367,7 @@ public struct Renderer: Sendable {
try render(contents: modifier.content, on: &result, within: context)
}
- /// Renders a environment value
+ /// Renders an environment value
///
/// - Parameter value: The environment value to resolve
///
@@ -410,7 +410,7 @@ public struct Renderer: Sendable {
}
}
- /// Renders a environment value
+ /// Renders an environment value
///
/// - Parameter value: The environment value to resolve
///
@@ -453,7 +453,7 @@ public struct Renderer: Sendable {
}
}
- /// Renders a environment statement
+ /// Renders an environment statement
///
/// - Parameter statement: The statement to resolve
///
@@ -574,7 +574,7 @@ public struct Renderer: Sendable {
}
}
- /// Renders a environment string
+ /// Renders an environment string
///
/// - Parameter envstring: The string to render
///
@@ -740,7 +740,7 @@ public struct Renderer: Sendable {
result += "\(element.name)>"
}
- /// Renders an environment string within a environment loop
+ /// Renders an environment string within an environment loop
///
/// - Parameters:
/// - envstring: The environment string to render
diff --git a/Sources/HTMLKitVapor/Configuration.swift b/Sources/HTMLKitVapor/Configuration.swift
index efbd9d64..fc189c1a 100644
--- a/Sources/HTMLKitVapor/Configuration.swift
+++ b/Sources/HTMLKitVapor/Configuration.swift
@@ -1,22 +1,45 @@
import HTMLKit
+import Vapor
-/// A type that holds configuration for the renderer
-public final class Configuration: @unchecked Sendable {
+/// A type that holds configuration for the renderer.
+internal final class Configuration: @unchecked Sendable {
- /// Holds the localization configuration
- internal var localization: HTMLKit.Localization
+ /// Holds the localization configuration.
+ internal var localization: Localization
- /// Holds the environment configuration
+ /// Holds the environment configuration.
internal var environment: HTMLKit.Environment
- /// Holds the enabled features
- internal var features: HTMLKit.Features
+ /// Holds the enabled features.
+ internal var features: Features
- /// Initializes a configuration
- public init() {
+ /// Create a configuration.
+ internal init() {
self.localization = Localization()
self.environment = Environment()
self.features = [.escaping]
}
+
+ /// Create a configuration.
+ ///
+ /// - Parameters:
+ /// - localization: The localization configuration to hold.
+ /// - environment: The environment configuration to hold.
+ /// - features: The enabled features to hold.
+ internal init(localization: Localization, environment: HTMLKit.Environment, features: Features) {
+
+ self.localization = localization
+ self.environment = environment
+ self.features = features
+ }
+}
+
+extension Configuration {
+
+ /// The key used to store the configuration in the storage.
+ internal struct StorageKey: Vapor.StorageKey {
+
+ internal typealias Value = Configuration
+ }
}
diff --git a/Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift b/Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift
index 2e93ecec..69fa0dd9 100644
--- a/Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift
+++ b/Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift
@@ -3,15 +3,15 @@ import Vapor
extension Application {
- /// Provides access to the provider for the application
+ /// Access the provider through the application.
public var htmlkit: HtmlKit {
return .init(application: self)
}
- /// Represents the provider for Vapor
+ /// A type that represents the provider.
public struct HtmlKit: Sendable {
- /// Manages environment settings
+ /// Manage the environment for the application.
public var environment: HTMLKit.Environment {
get {
@@ -23,7 +23,7 @@ extension Application {
}
}
- /// Manages localization settings
+ /// Manage the localization for the application.
public var localization: HTMLKit.Localization {
get {
@@ -35,7 +35,7 @@ extension Application {
}
}
- /// Manages feature flags
+ /// Manage the features for the application.
public var features: HTMLKit.Features {
get {
@@ -47,32 +47,26 @@ extension Application {
}
}
- /// The key used to store the configuration in Vapor's storage
- private struct ConfigurationKey: StorageKey {
+ /// The configuration for the renderer.
+ private var configuration: Configuration {
- public typealias Value = Configuration
- }
-
- /// The configuration for the view renderer
- internal var configuration: Configuration {
-
- if let configuration = application.storage[ConfigurationKey.self] {
+ if let configuration = application.storage[Configuration.StorageKey.self] {
return configuration
}
let configuration = Configuration()
- application.storage[ConfigurationKey.self] = configuration
+ application.storage[Configuration.StorageKey.self] = configuration
return configuration
}
- /// Holds a reference to the Vapor application
- internal let application: Application
+ /// Holds a reference to the application.
+ private let application: Application
- /// Initializes the HTMLKit provider for a given Vapor application
+ /// Create the provider.
///
- /// - Parameter application: The application instance from Vapor
+ /// - Parameter application: The application to provide for.
public init(application: Application) {
self.application = application
@@ -82,9 +76,83 @@ extension Application {
extension Request {
- /// Access to the view renderer
- public var htmlkit: ViewRenderer {
- return .init(eventLoop: eventLoop, configuration: application.htmlkit.configuration, logger: logger)
+ /// Access the provider through the request.
+ public var htmlkit: HtmlKit {
+ return .init(request: self)
+ }
+
+ /// A type that represents the provider.
+ public struct HtmlKit: Sendable {
+
+ /// Manage the environment for the request.
+ public var environment: HTMLKit.Environment {
+
+ get {
+ configuration.environment
+ }
+
+ nonmutating set {
+ configuration.environment = newValue
+ }
+ }
+
+ /// The configuration for the renderer.
+ private var configuration: Configuration {
+
+ if let configuration = request.storage[Configuration.StorageKey.self] {
+ return configuration
+ }
+
+ let configuration = Configuration(localization: request.application.htmlkit.localization, environment: HTMLKit.Environment(from: request.application.htmlkit.environment), features: request.application.htmlkit.features)
+
+ request.storage[Configuration.StorageKey.self] = configuration
+
+ return configuration
+ }
+
+ /// Holds a reference to the request.
+ private let request: Request
+
+ /// Create the provider.
+ ///
+ /// - Parameter request: The request to provide for.
+ public init(request: Request) {
+
+ self.request = request
+ }
+
+ /// Render the view and transform it into a response.
+ ///
+ /// - Parameter view: The view to render.
+ ///
+ /// - Returns: The response.
+ public func render(_ view: some HTMLKit.View) -> EventLoopFuture {
+
+ let renderer = Renderer(localization: configuration.localization,
+ environment: configuration.environment,
+ features: configuration.features,
+ logger: request.logger)
+
+ do {
+
+ var buffer = ByteBufferAllocator().buffer(capacity: 4096)
+ buffer.writeString(try renderer.render(view: view))
+
+ return request.eventLoop.makeSucceededFuture(View(data: buffer))
+
+ } catch(let error) {
+ return request.eventLoop.makeFailedFuture(error)
+ }
+ }
+
+ /// Render the view and transform it into a response.
+ ///
+ /// - Parameter view: The view to render.
+ ///
+ /// - Returns: The response.
+ public func render(_ view: some HTMLKit.View) async throws -> Vapor.View {
+ return try await render(view).get()
+ }
}
}
diff --git a/Sources/HTMLKitVapor/ViewRenderer.swift b/Sources/HTMLKitVapor/ViewRenderer.swift
deleted file mode 100644
index c6b63bf7..00000000
--- a/Sources/HTMLKitVapor/ViewRenderer.swift
+++ /dev/null
@@ -1,59 +0,0 @@
-import HTMLKit
-import Vapor
-
-/// A type responsible for rendering views in Vapor using HTMLKit
-public struct ViewRenderer: Sendable {
-
- /// The event loop that the renderer operates on
- internal let eventLoop: EventLoop
-
- /// The actual renderer
- internal let renderer: Renderer
-
- /// The logger used to log all operations
- private let logger: Logger
-
- /// Initializes the view renderer
- ///
- /// - Parameters:
- /// - eventLoop: The event loop used for asynchronous operations
- /// - configuration: The configuration for the renderer
- /// - logger: The logger used to log all operations
- public init(eventLoop: EventLoop, configuration: Configuration, logger: Logger) {
-
- self.eventLoop = eventLoop
- self.renderer = Renderer(localization: configuration.localization,
- environment: configuration.environment,
- features: configuration.features,
- logger: logger)
- self.logger = logger
- }
-
- /// Renders the given view and transforms it into a view response
- ///
- /// - Parameter view: The view to render
- ///
- /// - Returns: The view response
- public func render(_ view: some HTMLKit.View) -> EventLoopFuture {
-
- do {
-
- var buffer = ByteBufferAllocator().buffer(capacity: 4096)
- buffer.writeString(try renderer.render(view: view))
-
- return eventLoop.makeSucceededFuture(View(data: buffer))
-
- } catch(let error) {
- return eventLoop.makeFailedFuture(error)
- }
- }
-
- /// Renders the given view and transforms it into a view response
- ///
- /// - Parameter view: The view to render
- ///
- /// - Returns: The view response
- public func render(_ view: some HTMLKit.View) async throws -> Vapor.View {
- return try await render(view).get()
- }
-}
diff --git a/Tests/HTMLKitTests/SecurityTests.swift b/Tests/HTMLKitTests/SecurityTests.swift
index 5fd23c0b..da196f8e 100644
--- a/Tests/HTMLKitTests/SecurityTests.swift
+++ b/Tests/HTMLKitTests/SecurityTests.swift
@@ -42,7 +42,7 @@ struct SecurityTests {
)
}
- /// Tests the escaping within a environment context.
+ /// Tests the escaping within an environment context.
///
/// The renderer is expected to proceed as in an html context.
@Test
diff --git a/Tests/HTMLKitVaporTests/ProviderTests.swift b/Tests/HTMLKitVaporTests/ProviderTests.swift
index 228dfaff..50c66623 100644
--- a/Tests/HTMLKitVaporTests/ProviderTests.swift
+++ b/Tests/HTMLKitVaporTests/ProviderTests.swift
@@ -246,7 +246,7 @@ struct ProviderTests {
if let languages = request.headers.first(name: .acceptLanguage) {
if let language = languages.components(separatedBy: ",").first {
- app.htmlkit.environment.upsert(HTMLKit.Locale(tag: language), for: \EnvironmentKeys.locale)
+ request.htmlkit.environment.upsert(HTMLKit.Locale(tag: language), for: \EnvironmentKeys.locale)
}
}
@@ -294,7 +294,7 @@ struct ProviderTests {
if let languages = request.headers.first(name: .acceptLanguage) {
if let language = languages.components(separatedBy: ",").first {
- request.application.htmlkit.environment.upsert(HTMLKit.Locale(tag: language), for: \EnvironmentKeys.locale)
+ request.htmlkit.environment.upsert(HTMLKit.Locale(tag: language), for: \EnvironmentKeys.locale)
}
}
@@ -344,14 +344,14 @@ struct ProviderTests {
app.get("test", "de") { request async throws -> Vapor.View in
- request.application.htmlkit.environment.upsert(HTMLKit.Locale(tag: "de-DE"), for: \EnvironmentKeys.locale)
+ request.htmlkit.environment.upsert(HTMLKit.Locale(tag: "de-DE"), for: \EnvironmentKeys.locale)
return try await request.htmlkit.render(TestPage.ChildView())
}
app.get("test", "fr") { request async throws -> Vapor.View in
- request.application.htmlkit.environment.upsert(HTMLKit.Locale(tag: "fr"), for: \EnvironmentKeys.locale)
+ request.htmlkit.environment.upsert(HTMLKit.Locale(tag: "fr"), for: \EnvironmentKeys.locale)
return try await request.htmlkit.render(TestPage.ChildView())
}
@@ -395,6 +395,63 @@ struct ProviderTests {
try await app.asyncShutdown()
}
+ /// Tests the concurrent localization behavior based on the accept languages of the client.
+ @Test
+ func testHeaderBasedConcurrentLocalization() async throws {
+
+ guard let source = Bundle.module.url(forResource: "Localization", withExtension: nil) else {
+ return
+ }
+
+ let app = try await Application.make(.testing)
+
+ app.htmlkit.localization.set(source: source)
+ app.htmlkit.localization.set(locale: "en-GB")
+
+ app.get("test") { request async throws -> Vapor.View in
+
+ if let languages = request.headers.first(name: .acceptLanguage) {
+
+ if let language = languages.components(separatedBy: ",").first {
+ request.htmlkit.environment.upsert(HTMLKit.Locale(tag: language), for: \EnvironmentKeys.locale)
+ }
+ }
+
+ return try await request.htmlkit.render(TestPage.ChildView())
+ }
+
+ let languages = ["fr": "Bonjour le monde", "en-GB": "Hiya World", "de-DE": "Hallo Welt"]
+
+ await withThrowingTaskGroup(of: Void.self) { group in
+
+ for language in languages {
+
+ group.addTask {
+
+ try await app.test(.GET, "test", headers: ["accept-language": language.key]) { response async in
+
+ #expect(response.status == .ok)
+ #expect(response.body.string ==
+ """
+ \
+ \
+ \
+ TestPage\
+ \
+ \
+ \(language.value)
\
+ \
+
+ """
+ )
+ }
+ }
+ }
+ }
+
+ try await app.asyncShutdown()
+ }
+
/// Tests the localization behavior when the preferred language is unknown.
///
/// A language is considered unknown if the locale couldn't be found (e.g. typo) or isn't set up in the first place.