Skip to content

Commit

Permalink
added code comments (#12)
Browse files Browse the repository at this point in the history
* added a ton of doc comments

* comment fixes

* added more comments
  • Loading branch information
sliemeobn authored Jul 2, 2024
1 parent ccc8052 commit 547d558
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 0 deletions.
28 changes: 28 additions & 0 deletions Sources/Elementary/Core/CoreModel.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
/// A type that represents HTML content that can be rendered.
///
/// You can create reusable HTML components by conforming to this protocol
/// and implementing the ``content`` property.
///
/// ```swift
/// struct FeatureList: HTML {
/// var features: [String]
///
/// var content: some HTML {
/// ul {
/// for feature in features {
/// li { feature }
/// }
/// }
/// }
/// }
/// ```
public protocol HTML<Tag> {
/// The HTML tag this component represents, if any.
///
/// The Tag type defines which attributes can be attached to an HTML element.
/// If an element does not represent a specific HTML tag, the Tag type will
/// be ``Swift/Never`` and the element cannot be attributed.
associatedtype Tag: HTMLTagDefinition = Content.Tag

/// The type of the HTML content this component represents.
associatedtype Content: HTML = Never

/// The HTML content of this component.
@HTMLBuilder var content: Content { get }

@_spi(Rendering)
Expand All @@ -11,7 +37,9 @@ public protocol HTML<Tag> {
static func _render<Renderer: _AsyncHTMLRendering>(_ html: consuming Self, into renderer: inout Renderer, with context: consuming _RenderingContext) async throws
}

/// A type that represents an HTML tag.
public protocol HTMLTagDefinition {
/// The name of the HTML tag as it is rendered in an HTML document.
static var name: String { get }

@_spi(Rendering)
Expand Down
40 changes: 40 additions & 0 deletions Sources/Elementary/Core/Html+Attributes.swift
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
/// An HTML attribute that can be applied to an HTML element of the associated tag.
public struct HTMLAttribute<Tag: HTMLTagDefinition> {
var htmlAttribute: StoredAttribute

/// The name of the attribute.
public var name: String { htmlAttribute.name }

/// The value of the attribute.
public var value: String? { htmlAttribute.value }
}

/// The action to take when merging an attribute with the same name.
public struct HTMLAttributeMergeAction {
var mergeMode: StoredAttribute.MergeMode

/// Replaces the value of the existing attribute with the new value.
public static var replacing: Self { .init(mergeMode: .replaceValue) }

/// Ignores the new value if the attribute already exists.
public static var ignoring: Self { .init(mergeMode: .ignoreIfSet) }

/// Appends the new value to the existing value, separated by the specified string.
public static func appending(seperatedBy: String) -> Self { .init(mergeMode: .appendValue(seperatedBy)) }
}

public extension HTMLAttribute {
/// Creates a new HTML attribute with the specified name and value.
/// - Parameters:
/// - name: The name of the attribute.
/// - value: The value of the attribute.
/// - action: The merge action to use with a previously attached attribute with the same name.
init(name: String, value: String?, mergedBy action: HTMLAttributeMergeAction = .replacing) {
htmlAttribute = .init(name: name, value: value, mergeMode: action.mergeMode)
}

/// Changes the default merge action of this attribute.
/// - Parameter action: The new merge action to use.
/// - Returns: A modified attribute with the specified merge action.
consuming func mergedBy(_ action: HTMLAttributeMergeAction) -> HTMLAttribute {
.init(name: name, value: value, mergedBy: action)
}
Expand All @@ -42,28 +60,45 @@ public struct _AttributedElement<Content: HTML>: HTML {
}

public extension HTMLElement {
/// Creates a new HTML element with the specified attribute and content.
/// - Parameters:
/// - attribute: The attribute to apply to the element.
/// - content: The content of the element.
init(_ attribute: HTMLAttribute<Tag>, @HTMLBuilder content: () -> Content) {
attributes = .init(attribute)
self.content = content()
}

/// Creates a new HTML element with the specified attributes and content.
/// - Parameters:
/// - attributes: The attributes to apply to the element.
/// - content: The content of the element.
init(_ attributes: HTMLAttribute<Tag>..., @HTMLBuilder content: () -> Content) {
self.attributes = .init(attributes)
self.content = content()
}
}

public extension HTMLVoidElement {
/// Creates a new HTML void element with the specified attribute.
/// - Parameter attribute: The attribute to apply to the element.
init(_ attribute: HTMLAttribute<Tag>) {
attributes = .init(attribute)
}

/// Creates a new HTML void element with the specified attributes.
/// - Parameter attributes: The attributes to apply to the element.
init(_ attributes: HTMLAttribute<Tag>...) {
self.attributes = .init(attributes)
}
}

public extension HTML where Tag: HTMLTrait.Attributes.Global {
/// Adds the specified attribute to the element.
/// - Parameters:
/// - attribute: The attribute to add to the element.
/// - condition: If set to false, the attribute will not be added.
/// - Returns: A new element with the specified attribute added.
func attributes(_ attribute: HTMLAttribute<Tag>, when condition: Bool = true) -> _AttributedElement<Self> {
if condition {
return _AttributedElement(content: self, attributes: .init(attribute))
Expand All @@ -72,6 +107,11 @@ public extension HTML where Tag: HTMLTrait.Attributes.Global {
}
}

/// Adds the specified attributes to the element.
/// - Parameters:
/// - attributes: The attributes to add to the element.
/// - condition: If set to false, the attributes will not be added.
/// - Returns: A new element with the specified attributes added.
func attributes(_ attributes: HTMLAttribute<Tag>..., when condition: Bool = true) -> _AttributedElement<Self> {
_AttributedElement(content: self, attributes: .init(condition ? attributes : []))
}
Expand Down
19 changes: 19 additions & 0 deletions Sources/Elementary/Core/Html+Elements.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
/// An HTML element that can contain content.
public struct HTMLElement<Tag: HTMLTagDefinition, Content: HTML>: HTML where Tag: HTMLTrait.Paired {
/// The type of the HTML tag this element represents.
public typealias Tag = Tag
var attributes: AttributeStorage

// The content of the element.
public var content: Content

/// Creates a new HTML element with the specified content.
/// - Parameter content: The content of the element.
public init(@HTMLBuilder content: () -> Content) {
attributes = .init()
self.content = content()
Expand All @@ -25,10 +31,13 @@ public struct HTMLElement<Tag: HTMLTagDefinition, Content: HTML>: HTML where Tag
}
}

/// An HTML element that does not contain content.
public struct HTMLVoidElement<Tag: HTMLTagDefinition>: HTML where Tag: HTMLTrait.Unpaired {
/// The type of the HTML tag this element represents.
public typealias Tag = Tag
var attributes: AttributeStorage

/// Creates a new HTML void element.
public init() {
attributes = .init()
}
Expand All @@ -46,9 +55,14 @@ public struct HTMLVoidElement<Tag: HTMLTagDefinition>: HTML where Tag: HTMLTrait
}
}

/// A type that represents an HTML comment.
///
/// A comment is rendered as `<!--text-->` and the text will be escaped if necessary.
public struct HTMLComment: HTML {
/// The text of the comment.
public var text: String

/// Creates a new HTML comment with the specified text.
public init(_ text: String) {
self.text = text
}
Expand All @@ -66,9 +80,14 @@ public struct HTMLComment: HTML {
}
}

/// A type that represents custom raw, untyped HTML.
///
/// The text is rendered as-is without any validation or escaping.
public struct HTMLRaw: HTML {
/// The raw HTML text.
public var text: String

/// Creates a new raw HTML content with the specified text.
public init(_ text: String) {
self.text = text
}
Expand Down
7 changes: 7 additions & 0 deletions Sources/Elementary/Core/HtmlBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/// A result builder for building HTML components.
@resultBuilder public struct HTMLBuilder {
public static func buildExpression<Content>(_ content: Content) -> Content where Content: HTML {
content
Expand Down Expand Up @@ -66,6 +67,7 @@ extension Optional: HTML where Wrapped: HTML {
}
}

/// A type that represents empty HTML.
public struct EmptyHTML: HTML {
public init() {}

Expand All @@ -80,9 +82,14 @@ public struct EmptyHTML: HTML {
}
}

/// A type that represents text content in an HTML document.
///
/// The text will be escaped when rendered.
public struct HTMLText: HTML {
/// The text content.
public var text: String

/// Creates a new text content with the specified text.
public init(_ text: some StringProtocol) {
self.text = String(text)
}
Expand Down
23 changes: 23 additions & 0 deletions Sources/Elementary/Html+Rendering.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
public extension HTML {
/// Renders the HTML content into a single string.
/// - Returns: The rendered HTML content.
///
/// This method is synchronous and collects the entire rendered content into a single string.
/// For server applications, use the async API ``render(into:chunkSize:)`` instead.
consuming func render() -> String {
var renderer = HTMLTextRenderer()
Self._render(self, into: &renderer, with: .emptyContext)
return renderer.collect()
}

/// Renders the HTML content into a formatted string.
/// - Returns: The rendered HTML content.
///
/// Should only be used for testing and debugging purposes.
consuming func renderFormatted() -> String {
var renderer = PrettyHTMLTextRenderer(spaces: 2)
Self._render(self, into: &renderer, with: .emptyContext)
Expand All @@ -17,19 +26,33 @@ public extension HTML {
Self._render(self, into: &renderer, with: .emptyContext)
}

/// Renders the HTML content into a stream writer.
/// - Parameters:
/// - writer: The ``HTMLStreamWriter`` to write the rendered content to.
/// - chunkSize: The maximum size of the chunks to write to the stream.
///
/// This is the primary API for server-side applications to stream HTML content in HTTP responses.
consuming func render(into writer: some HTMLStreamWriter, chunkSize: Int = 1024) async throws {
var renderer = AsyncHTMLRenderer(writer: writer, chunkSize: chunkSize)
try await Self._render(self, into: &renderer, with: .emptyContext)
try await renderer.flush()
}

/// Renders the HTML content into a single string asynchronously.
/// - Returns: The rendered HTML content.
///
/// Only intended for testing purposes.
consuming func renderAsync() async throws -> String {
let writer = BufferWriter()
try await render(into: writer)
return String(decoding: writer.result, as: UTF8.self)
}
}

/// A type that write chunks of HTML content to a stream.
///
/// Conform to this protocol to stream HTML responses efficiently.
public protocol HTMLStreamWriter {
/// Writes a chunk of rendered HTML.
func write(_ bytes: ArraySlice<UInt8>) async throws
}
1 change: 1 addition & 0 deletions Sources/Elementary/HtmlAttributes.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/// A namespace for value types used in attributes.
public enum HTMLAttributeValue {}

// global attributes
Expand Down
20 changes: 20 additions & 0 deletions Sources/Elementary/HtmlDocument.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
/// A type that represents a full HTML document.
///
/// Provides a simple structure to model top-level HTML types.
/// A default ``content`` implementation takes your ``title``, ``head`` and ``body``
/// properties and renders them into a full HTML document.
///
/// ```swift
/// struct MyPage: HTMLDocument {
/// var title = "Hello, World!"
///
/// var head: some HTML {
/// meta(.name(.viewport), .content("width=device-width, initial-scale=1.0"))
/// }
///
/// var body: some HTML {
/// h1 { "Hello, World!" }
/// p { "This is a simple HTML document." }
/// }
/// }
/// ```
public protocol HTMLDocument: HTML {
associatedtype HTMLHead: HTML
associatedtype HTMLBody: HTML
Expand Down
9 changes: 9 additions & 0 deletions Sources/Elementary/HtmlTags.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
/// A namespace for HTML tag definitions.
public enum HTMLTag {}

/// A namespace for trait protocols that control the behavior a capabilities of HTML elements.
public enum HTMLTrait {
/// A namespace for traits that controls which attributes can go with a certain tag.
public enum Attributes {}
/// A marker that indicates that an HTML tag is paired.
public protocol Paired: HTMLTagDefinition, Attributes.Global {}
/// A marker that indicates that an HTML tag is unpaired.
public protocol Unpaired: HTMLTagDefinition, Attributes.Global {}
/// A marker that indicates that an HTML tag should be rendered inline (only controls whitespaces in formatted rendering).
public protocol RenderedInline {}
}

public extension HTMLTrait.Attributes {
/// A marker that indicates that an HTML tag can have global attributes.
///
/// Every HTML tag conforms to this.
protocol Global {}
}

Expand Down

0 comments on commit 547d558

Please sign in to comment.