From cf2eec3e6a7dfc0013b3656ade0ca7531f3d2a6f Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 10:59:28 +0100 Subject: [PATCH 01/48] pop rework --- LICENSE | 1 + Makefile | 9 - Package.swift | 61 +++--- Sources/SwiftHtml/v2/Attributes/Class.swift | 16 ++ Sources/SwiftHtml/v2/Attributes/Lang.swift | 16 ++ Sources/SwiftHtml/v2/Elements/Body.swift | 20 ++ Sources/SwiftHtml/v2/Elements/Br.swift | 15 ++ Sources/SwiftHtml/v2/Elements/Comment.swift | 16 ++ Sources/SwiftHtml/v2/Elements/Head.swift | 30 +++ Sources/SwiftHtml/v2/Elements/Html.swift | 38 ++++ Sources/SwiftHtml/v2/Elements/P.swift | 32 +++ Sources/SwiftHtml/v2/Elements/Title.swift | 15 ++ Sources/SwiftSgml/Attribute.swift | 17 -- Sources/SwiftSgml/Attributes/Custom.swift | 29 +++ Sources/SwiftSgml/Builder.swift | 43 ++++ Sources/SwiftSgml/Document.swift | 27 --- .../Document/Document+Renderer.swift | 99 +++++++++ Sources/SwiftSgml/Document/Document.swift | 37 ++++ Sources/SwiftSgml/DocumentRenderer.swift | 97 --------- Sources/SwiftSgml/Elements/Text.swift | 17 ++ Sources/SwiftSgml/EmptyTag.swift | 17 -- Sources/SwiftSgml/GroupTag.swift | 14 -- Sources/SwiftSgml/Interfaces/Attribute.swift | 18 ++ Sources/SwiftSgml/Interfaces/Attributed.swift | 48 +++++ .../SwiftSgml/Interfaces/Element+Short.swift | 15 ++ .../Interfaces/Element+ShortAttributed.swift | 16 ++ .../Interfaces/Element+Standard.swift | 42 ++++ .../Element+StandardAttributed.swift | 17 ++ Sources/SwiftSgml/Interfaces/Element.swift | 25 +++ Sources/SwiftSgml/Interfaces/Mutable.swift | 19 ++ Sources/SwiftSgml/Node.swift | 52 ----- Sources/SwiftSgml/Node/Node+Comment.swift | 16 ++ Sources/SwiftSgml/Node/Node+Short.swift | 19 ++ Sources/SwiftSgml/Node/Node+Standard.swift | 19 ++ Sources/SwiftSgml/Node/Node+Text.swift | 16 ++ Sources/SwiftSgml/Node/Node.swift | 13 ++ Sources/SwiftSgml/Tag.swift | 100 --------- Sources/SwiftSgml/TagBuilder.swift | 35 --- Tests/SwiftSgmlTests/AttributeTests.swift | 54 +++++ Tests/SwiftSgmlTests/CustomTags.swift | 20 -- .../SwiftSgmlTests/ElementBuilderTests.swift | 202 ++++++++++++++++++ Tests/SwiftSgmlTests/ElementTests.swift | 80 +++++++ Tests/SwiftSgmlTests/NodeTests.swift | 27 --- Tests/SwiftSgmlTests/SwiftSgmlTests.swift | 44 +--- Tests/SwiftSgmlTests/TagBuilderTests.swift | 199 ----------------- Tests/SwiftSgmlTests/TagTests.swift | 26 --- Tests/SwiftSgmlTests/Utils/Branch.swift | 19 ++ Tests/SwiftSgmlTests/Utils/Leaf.swift | 21 ++ Tests/SwiftSgmlTests/Utils/Root.swift | 36 ++++ .../SwiftSgmlTests/Utils/String+Minify.swift | 15 ++ .../Utils/XCTAssertDocument.swift | 20 ++ Tests/SwiftSgmlTests/assert.swift | 17 -- 52 files changed, 1189 insertions(+), 727 deletions(-) create mode 100644 Sources/SwiftHtml/v2/Attributes/Class.swift create mode 100644 Sources/SwiftHtml/v2/Attributes/Lang.swift create mode 100644 Sources/SwiftHtml/v2/Elements/Body.swift create mode 100644 Sources/SwiftHtml/v2/Elements/Br.swift create mode 100644 Sources/SwiftHtml/v2/Elements/Comment.swift create mode 100644 Sources/SwiftHtml/v2/Elements/Head.swift create mode 100644 Sources/SwiftHtml/v2/Elements/Html.swift create mode 100644 Sources/SwiftHtml/v2/Elements/P.swift create mode 100644 Sources/SwiftHtml/v2/Elements/Title.swift delete mode 100644 Sources/SwiftSgml/Attribute.swift create mode 100644 Sources/SwiftSgml/Attributes/Custom.swift create mode 100644 Sources/SwiftSgml/Builder.swift delete mode 100644 Sources/SwiftSgml/Document.swift create mode 100644 Sources/SwiftSgml/Document/Document+Renderer.swift create mode 100644 Sources/SwiftSgml/Document/Document.swift delete mode 100644 Sources/SwiftSgml/DocumentRenderer.swift create mode 100644 Sources/SwiftSgml/Elements/Text.swift delete mode 100644 Sources/SwiftSgml/EmptyTag.swift delete mode 100644 Sources/SwiftSgml/GroupTag.swift create mode 100644 Sources/SwiftSgml/Interfaces/Attribute.swift create mode 100644 Sources/SwiftSgml/Interfaces/Attributed.swift create mode 100644 Sources/SwiftSgml/Interfaces/Element+Short.swift create mode 100644 Sources/SwiftSgml/Interfaces/Element+ShortAttributed.swift create mode 100644 Sources/SwiftSgml/Interfaces/Element+Standard.swift create mode 100644 Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift create mode 100644 Sources/SwiftSgml/Interfaces/Element.swift create mode 100644 Sources/SwiftSgml/Interfaces/Mutable.swift delete mode 100644 Sources/SwiftSgml/Node.swift create mode 100644 Sources/SwiftSgml/Node/Node+Comment.swift create mode 100644 Sources/SwiftSgml/Node/Node+Short.swift create mode 100644 Sources/SwiftSgml/Node/Node+Standard.swift create mode 100644 Sources/SwiftSgml/Node/Node+Text.swift create mode 100644 Sources/SwiftSgml/Node/Node.swift delete mode 100644 Sources/SwiftSgml/Tag.swift delete mode 100644 Sources/SwiftSgml/TagBuilder.swift delete mode 100644 Tests/SwiftSgmlTests/CustomTags.swift create mode 100644 Tests/SwiftSgmlTests/ElementBuilderTests.swift create mode 100644 Tests/SwiftSgmlTests/ElementTests.swift delete mode 100644 Tests/SwiftSgmlTests/NodeTests.swift delete mode 100644 Tests/SwiftSgmlTests/TagBuilderTests.swift delete mode 100644 Tests/SwiftSgmlTests/TagTests.swift create mode 100644 Tests/SwiftSgmlTests/Utils/Branch.swift create mode 100644 Tests/SwiftSgmlTests/Utils/Leaf.swift create mode 100644 Tests/SwiftSgmlTests/Utils/Root.swift create mode 100644 Tests/SwiftSgmlTests/Utils/String+Minify.swift create mode 100644 Tests/SwiftSgmlTests/Utils/XCTAssertDocument.swift delete mode 100644 Tests/SwiftSgmlTests/assert.swift diff --git a/LICENSE b/LICENSE index ca9fb45..9a07ee3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2018-2022 Tibor Bödecs +Copyright (c) 2022-2024 Binary Birds Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/Makefile b/Makefile index fd99366..53f8dfa 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,2 @@ test: swift test --enable-test-discovery --parallel - -docs: - jazzy \ - --clean \ - --author "Tibor Bödecs" \ - --author_url https://twitter.com/tiborbodecs/ \ - --module-version 1.6.0 \ - --module SwiftHtml \ - --output docs/ diff --git a/Package.swift b/Package.swift index a7c00a6..5ca2250 100644 --- a/Package.swift +++ b/Package.swift @@ -1,50 +1,51 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.9 import PackageDescription let package = Package( name: "swift-html", platforms: [ - .macOS(.v10_15) + .macOS(.v10_15), ], products: [ .library(name: "SwiftSgml", targets: ["SwiftSgml"]), - .library(name: "SwiftHtml", targets: ["SwiftHtml"]), - .library(name: "SwiftSvg", targets: ["SwiftSvg"]), - .library(name: "SwiftSitemap", targets: ["SwiftSitemap"]), - .library(name: "SwiftRss", targets: ["SwiftRss"]), +// .library(name: "SwiftHtml", targets: ["SwiftHtml"]), +// .library(name: "SwiftSvg", targets: ["SwiftSvg"]), +// .library(name: "SwiftSitemap", targets: ["SwiftSitemap"]), +// .library(name: "SwiftRss", targets: ["SwiftRss"]), ], dependencies: [ ], targets: [ .target(name: "SwiftSgml", dependencies: []), - .target(name: "SwiftHtml", dependencies: [ - .target(name: "SwiftSgml") - ]), - .target(name: "SwiftSvg", dependencies: [ - .target(name: "SwiftSgml") - ]), - .target(name: "SwiftSitemap", dependencies: [ - .target(name: "SwiftSgml") - ]), - .target(name: "SwiftRss", dependencies: [ - .target(name: "SwiftSgml") - ]), +// .target(name: "SwiftHtml", dependencies: [ +// .target(name: "SwiftSgml") +// ]), +// .target(name: "SwiftSvg", dependencies: [ +// .target(name: "SwiftSgml") +// ]), +// .target(name: "SwiftSitemap", dependencies: [ +// .target(name: "SwiftSgml") +// ]), +// .target(name: "SwiftRss", dependencies: [ +// .target(name: "SwiftSgml") +// ]), +// .testTarget(name: "SwiftSgmlTests", dependencies: [ .target(name: "SwiftSgml"), ]), - .testTarget(name: "SwiftHtmlTests", dependencies: [ - .target(name: "SwiftHtml"), - ]), - .testTarget(name: "SwiftSvgTests", dependencies: [ - .target(name: "SwiftSvg"), - ]), - .testTarget(name: "SwiftSitemapTests", dependencies: [ - .target(name: "SwiftSitemap"), - ]), - .testTarget(name: "SwiftRssTests", dependencies: [ - .target(name: "SwiftRss"), - ]), +// .testTarget(name: "SwiftHtmlTests", dependencies: [ +// .target(name: "SwiftHtml"), +// ]), +// .testTarget(name: "SwiftSvgTests", dependencies: [ +// .target(name: "SwiftSvg"), +// ]), +// .testTarget(name: "SwiftSitemapTests", dependencies: [ +// .target(name: "SwiftSitemap"), +// ]), +// .testTarget(name: "SwiftRssTests", dependencies: [ +// .target(name: "SwiftRss"), +// ]), ] ) diff --git a/Sources/SwiftHtml/v2/Attributes/Class.swift b/Sources/SwiftHtml/v2/Attributes/Class.swift new file mode 100644 index 0000000..5f0b29f --- /dev/null +++ b/Sources/SwiftHtml/v2/Attributes/Class.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct Class: Attribute { + public let key: String + public let value: String? + + public init(_ value: String? = nil) { + self.key = "class" + self.value = value + } +} diff --git a/Sources/SwiftHtml/v2/Attributes/Lang.swift b/Sources/SwiftHtml/v2/Attributes/Lang.swift new file mode 100644 index 0000000..aa756b6 --- /dev/null +++ b/Sources/SwiftHtml/v2/Attributes/Lang.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct Lang: Attribute { + public let key: String + public let value: String? + + public init(_ value: String? = nil) { + self.key = "lang" + self.value = value + } +} diff --git a/Sources/SwiftHtml/v2/Elements/Body.swift b/Sources/SwiftHtml/v2/Elements/Body.swift new file mode 100644 index 0000000..e78ac89 --- /dev/null +++ b/Sources/SwiftHtml/v2/Elements/Body.swift @@ -0,0 +1,20 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct Body: StandardAttributedElement { + + public let children: [Element] + public var attributes: [Attribute] + + public init( + @Builder attributes b2: () -> [Attribute] = { [] }, + @Builder elements b1: () -> [Element] + ) { + self.children = b1() + self.attributes = b2() + } +} diff --git a/Sources/SwiftHtml/v2/Elements/Br.swift b/Sources/SwiftHtml/v2/Elements/Br.swift new file mode 100644 index 0000000..218a8e5 --- /dev/null +++ b/Sources/SwiftHtml/v2/Elements/Br.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +struct Br: ShortAttributedElement { + + var attributes: [Attribute] + + init() { + self.attributes = [] + } +} diff --git a/Sources/SwiftHtml/v2/Elements/Comment.swift b/Sources/SwiftHtml/v2/Elements/Comment.swift new file mode 100644 index 0000000..2f0f4d0 --- /dev/null +++ b/Sources/SwiftHtml/v2/Elements/Comment.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct Comment: Element { + + let value: String + public init(_ value: String) { + self.value = value + } + + public var node: Node { .comment(.init(value: value)) } +} diff --git a/Sources/SwiftHtml/v2/Elements/Head.swift b/Sources/SwiftHtml/v2/Elements/Head.swift new file mode 100644 index 0000000..dd11335 --- /dev/null +++ b/Sources/SwiftHtml/v2/Elements/Head.swift @@ -0,0 +1,30 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol HeadChildElement: Element {} + +extension Title: HeadChildElement {} +extension Comment: HeadChildElement {} + +public protocol HeadAttribute: Attribute {} + +extension Lang: HeadAttribute {} +extension Custom: HeadAttribute {} + +public struct Head: StandardAttributedElement { + + public let children: [Element] + public var attributes: [Attribute] + + public init( + @Builder attributes b2: () -> [HeadAttribute] = { [] }, + @Builder elements b1: () -> [HeadChildElement] + ) { + self.children = b1() + self.attributes = b2() + } +} diff --git a/Sources/SwiftHtml/v2/Elements/Html.swift b/Sources/SwiftHtml/v2/Elements/Html.swift new file mode 100644 index 0000000..09c59c5 --- /dev/null +++ b/Sources/SwiftHtml/v2/Elements/Html.swift @@ -0,0 +1,38 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol HtmlChildElement: Element {} + +extension Head: HtmlChildElement {} +extension Body: HtmlChildElement {} +extension Comment: HtmlChildElement {} + +public protocol HtmlAttribute: Attribute {} + +extension Lang: HtmlAttribute {} +extension Custom: HtmlAttribute {} + +public struct Html: StandardAttributedElement { + + public let children: [Element] + public var attributes: [Attribute] + + public init( + @Builder attributes b2: () -> [HtmlAttribute] = { [] }, + @Builder elements b1: () -> [HtmlChildElement] + ) { + self.children = b1() + self.attributes = b2() + } +} + +extension Html { + + func lang(_ value: String) -> Self { + modify { $0.addAttribute(Lang(value)) } + } +} diff --git a/Sources/SwiftHtml/v2/Elements/P.swift b/Sources/SwiftHtml/v2/Elements/P.swift new file mode 100644 index 0000000..71e4b39 --- /dev/null +++ b/Sources/SwiftHtml/v2/Elements/P.swift @@ -0,0 +1,32 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct P: StandardAttributedElement { + + public let children: [Element] + public var attributes: [Attribute] + + public init( + @Builder _ elementBuilder: () -> [Element], + @Builder attributes attributeBuilder: () -> [Attribute] = { [] } + ) { + self.children = elementBuilder() + self.attributes = attributeBuilder() + } + + public init( + _ value: String, + @Builder attributes attributeBuilder: () -> [Attribute] = { [] } + ) { + self.children = [ + Text(value) + ] + self.attributes = [] + } + + +} diff --git a/Sources/SwiftHtml/v2/Elements/Title.swift b/Sources/SwiftHtml/v2/Elements/Title.swift new file mode 100644 index 0000000..df50b6f --- /dev/null +++ b/Sources/SwiftHtml/v2/Elements/Title.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct Title: StandardElement { + + public let children: [Element] + + public init(_ value: String) { + self.children = [Text(value)] + } +} diff --git a/Sources/SwiftSgml/Attribute.swift b/Sources/SwiftSgml/Attribute.swift deleted file mode 100644 index 1242cde..0000000 --- a/Sources/SwiftSgml/Attribute.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// Attribute.swift -// SwiftSgml -// -// Created by Tibor Bodecs on 2021. 07. 19.. -// - -public struct Attribute { - - public var key: String - public var value: String? - - public init(key: String, value: String? = nil) { - self.key = key - self.value = value - } -} diff --git a/Sources/SwiftSgml/Attributes/Custom.swift b/Sources/SwiftSgml/Attributes/Custom.swift new file mode 100644 index 0000000..602d9b4 --- /dev/null +++ b/Sources/SwiftSgml/Attributes/Custom.swift @@ -0,0 +1,29 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct Custom: Attribute { + public let key: String + public var value: String? + + public init(key: String, value: String? = nil) { + self.key = key + self.value = value + } +} + +public struct Flag: Attribute { + + public let key: String + public var value: String? { + get { nil } + set {} + } + + public init(_ key: String) { + self.key = key + } +} diff --git a/Sources/SwiftSgml/Builder.swift b/Sources/SwiftSgml/Builder.swift new file mode 100644 index 0000000..a7449d5 --- /dev/null +++ b/Sources/SwiftSgml/Builder.swift @@ -0,0 +1,43 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 12/11/2023. +// + +@resultBuilder +public enum Builder { + + public static func buildBlock(_ components: T...) -> [T] { + components + } +} + +//@resultBuilder +//public enum TagBuilder { +// +// public static func buildBlock(_ components: [Tag]...) -> Tag { +// GroupTag(components.flatMap { $0 }) +// } +// +// public static func buildBlock(_ components: Tag...) -> Tag { +// GroupTag(components) +// } +// +// public static func buildOptional(_ component: Tag?) -> Tag { +// component ?? GroupTag() +// } +// +// public static func buildEither(first component: Tag) -> Tag { +// component +// } +// +// public static func buildEither(second component: Tag) -> Tag { +// component +// } +// +// public static func buildArray(_ components: [Tag]) -> Tag { +// GroupTag(components) +// } +//} +// diff --git a/Sources/SwiftSgml/Document.swift b/Sources/SwiftSgml/Document.swift deleted file mode 100644 index cd564f8..0000000 --- a/Sources/SwiftSgml/Document.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// Document.swift -// SwiftSgml -// -// Created by Tibor Bodecs on 2021. 07. 19.. -// - -public struct Document { - - public enum `Type` { - case unspecified - case html - case xml - case custom(String) - } - - public let type: `Type` - public let root: Tag - - public init(_ type: `Type` = .unspecified, @TagBuilder _ builder: () -> Tag) { - self.type = type - self.root = builder() - } -} - - - diff --git a/Sources/SwiftSgml/Document/Document+Renderer.swift b/Sources/SwiftSgml/Document/Document+Renderer.swift new file mode 100644 index 0000000..fd9b403 --- /dev/null +++ b/Sources/SwiftSgml/Document/Document+Renderer.swift @@ -0,0 +1,99 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + + + +public struct DocumentRenderer { + + public struct Options: OptionSet, Sendable { + + /// Produce human-readable HTML with indented output. + public static let prettyPrinted = Options(rawValue: 1 << 0) + + /// The format's default value. + public let rawValue: UInt + + /// Creates an OutputFormatting value with the given raw value. + public init(rawValue: UInt) { + self.rawValue = rawValue + } + } + + public var options: Options + + public init(options: Options = []) { + self.options = options + } + + // TODO: implement nice rendering... + private var prettyPrint: Bool { + options.contains(.prettyPrinted) + } + + public func render(_ document: Document) -> String { + var result: [String] = [] + + result += [renderDocumentType(document.type)] + result += render(document.root.node) + + return result.joined(separator: prettyPrint ? "\n" : "") + } +} + +private extension DocumentRenderer { + + func renderDocumentType(_ type: Document.`Type`) -> String { + switch type { + case .unspecified: + return "" + case .html: + return "" + case .xml: + return #""# + case let .custom(value): + return value + } + } + + func render(_ node: Node) -> [String] { + var result: [String] = [] + switch node { + case .standard(let node, let children): + result += ["<\(node.name)\(attr(node.attributes))>"] + for child in children { + result += render(child) + } + result += [""] + + + case .short(let node): + result += ["<\(node.name)\(attr(node.attributes))>"] + case .text(let node): + result += ["\(node.value)"] + case .comment(let node): + result += [""] + } + return result + } + + func attr(_ attributes: [any Attribute]) -> String { + let attr = render(attributes).joined(separator: " ") + if !attr.isEmpty { + return " " + attr + } + return attr + } + + func render(_ attributes: [any Attribute]) -> [String] { + attributes.map { attribute in + if let value = attribute.value { + return #"\#(attribute.key)="\#(value)""# + } + return attribute.key + } + } +} diff --git a/Sources/SwiftSgml/Document/Document.swift b/Sources/SwiftSgml/Document/Document.swift new file mode 100644 index 0000000..c912ebd --- /dev/null +++ b/Sources/SwiftSgml/Document/Document.swift @@ -0,0 +1,37 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import Foundation + +public struct Document { + + public enum `Type` { + case unspecified + case html + case xml + case custom(String) + } + + public let type: `Type` + public let root: Element + + public init( + _ type: `Type` = .unspecified, + _ rootElementBuilder: () -> Element + ) { + self.type = type + self.root = rootElementBuilder() + } + + public init( + type: `Type` = .unspecified, + root: Element + ) { + self.type = type + self.root = root + } +} diff --git a/Sources/SwiftSgml/DocumentRenderer.swift b/Sources/SwiftSgml/DocumentRenderer.swift deleted file mode 100644 index 8373817..0000000 --- a/Sources/SwiftSgml/DocumentRenderer.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// DocumentRenderer.swift -// SwiftSgml -// -// Created by Tibor Bodecs on 2021. 11. 19.. -// - -public struct DocumentRenderer { - - private let newline: String - public let minify: Bool - public let indent: Int - public let selfClose: Bool - - public init(minify: Bool = false, indent: Int = 4, selfClose: Bool = false) { - self.minify = minify - self.indent = minify ? 0 : indent - self.newline = minify ? "" : "\n" - self.selfClose = selfClose - } - - public func render(_ document: Document) -> String { - renderDocumentType(document.type) + render(tag: document.root) - } - - // MARK: - private render methods - - private func renderDocumentType(_ type: Document.`Type`) -> String { - switch type { - case .unspecified: - return "" - case .html: - return "" + newline - case .xml: - return #""# + newline - case let .custom(value): - return value + newline - } - } - - private func render(tag: Tag, level: Int = 0) -> String { - let spaces = String(repeating: " ", count: level * indent) - switch tag.node.type { - case .standard: - return spaces + renderOpening(tag) + (tag.node.contents ?? "") + renderChildren(tag, level: level, spaces: spaces) + renderClosing(tag) - case .comment: - return spaces + "" - case .empty: - if selfClose { - return spaces + renderSelfClosing(tag) - } else { - return spaces + renderOpening(tag) - } - case .group: - var contents = "" - if let rawValue = tag.node.contents { - contents = spaces + rawValue - } - return contents + renderChildren(tag, level: level, spaces: spaces, isGrouped: true) - } - } - - private func renderChildren(_ tag: Tag, level: Int, spaces: String, isGrouped: Bool = false) -> String { - var newLevel = level + 1 - if isGrouped { - newLevel = level - } - var children = tag.children.map { render(tag: $0, level: newLevel) }.joined(separator: newline) - if !children.isEmpty { - if !isGrouped { - children = newline + children + newline + spaces - } - } - return children - } - - private func renderOpening(_ tag: Tag) -> String { - return "<" + tag.node.name! + (tag.node.attributes.isEmpty ? "" : " ") + renderAttributes(tag.node.attributes) + ">" - } - - private func renderSelfClosing(_ tag: Tag) -> String { - return "<" + tag.node.name! + (tag.node.attributes.isEmpty ? "" : " ") + renderAttributes(tag.node.attributes) + " />" - } - - private func renderClosing(_ tag: Tag) -> String { - "" - } - - private func renderAttributes(_ attributes: [Attribute]) -> String { - return attributes.reduce([]) { res, next in - if let value = next.value { - return res + [next.key + #"=""# + value + #"""#] - } - return res + [next.key] - }.joined(separator: " ") - } -} diff --git a/Sources/SwiftSgml/Elements/Text.swift b/Sources/SwiftSgml/Elements/Text.swift new file mode 100644 index 0000000..8d33be7 --- /dev/null +++ b/Sources/SwiftSgml/Elements/Text.swift @@ -0,0 +1,17 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct Text: Element { + + let value: String + + public init(_ value: String) { + self.value = value + } + + public var node: Node { .text(.init(value: value)) } +} diff --git a/Sources/SwiftSgml/EmptyTag.swift b/Sources/SwiftSgml/EmptyTag.swift deleted file mode 100644 index 2083a29..0000000 --- a/Sources/SwiftSgml/EmptyTag.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 2022. 02. 11.. -// - -open class EmptyTag: Tag { - - open override class func createNode() -> Node { - Node(type: .empty, name: String(describing: self).lowercased()) - } - - public init() { - super.init() - } -} diff --git a/Sources/SwiftSgml/GroupTag.swift b/Sources/SwiftSgml/GroupTag.swift deleted file mode 100644 index 41f3b36..0000000 --- a/Sources/SwiftSgml/GroupTag.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 2022. 02. 11.. -// - - -open class GroupTag: Tag { - - open override class func createNode() -> Node { - .init(type: .group) - } -} diff --git a/Sources/SwiftSgml/Interfaces/Attribute.swift b/Sources/SwiftSgml/Interfaces/Attribute.swift new file mode 100644 index 0000000..5029b1f --- /dev/null +++ b/Sources/SwiftSgml/Interfaces/Attribute.swift @@ -0,0 +1,18 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol Attribute: Sendable { + var key: String { get } + var value: String? { get set } +} + +//extension Attribute { +// +// public static func == (lhs: Self, rhs: Self) -> Bool { +// lhs.key == rhs.key +// } +//} diff --git a/Sources/SwiftSgml/Interfaces/Attributed.swift b/Sources/SwiftSgml/Interfaces/Attributed.swift new file mode 100644 index 0000000..ccfff29 --- /dev/null +++ b/Sources/SwiftSgml/Interfaces/Attributed.swift @@ -0,0 +1,48 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol Attributed: Mutable { + + var attributes: [any Attribute] { get set } + +// mutating func addAttribute(_ attribute: Attribute) + + func add(attribute: any Attribute) -> Self + func remove(attributeByKey: String) -> Self +} + +extension Attributed { + + func index(of attribute: any Attribute) -> [any Attribute].Index? { + attributes.firstIndex(where: { $0.key == attribute.key }) + } + + mutating func add(attribute: any Attribute) { + if let index = index(of: attribute) { + attributes[index].value = attribute.value + } + else { + attributes.append(attribute) + } + } + + mutating func remove(attributeByKey key: String) { + attributes = attributes.filter { $0.key != key} + } + + // MARK: - + + public func add(attribute: any Attribute) -> Self { + mutate { $0.add(attribute: attribute) } + } + + public func remove(attributeByKey key: String) -> Self { + mutate { $0.remove(attributeByKey: key) } + } +} + + diff --git a/Sources/SwiftSgml/Interfaces/Element+Short.swift b/Sources/SwiftSgml/Interfaces/Element+Short.swift new file mode 100644 index 0000000..9347f40 --- /dev/null +++ b/Sources/SwiftSgml/Interfaces/Element+Short.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol ShortElement: Element {} + +extension ShortElement { + + public var node: Node { + .short(.init(name: name)) + } +} diff --git a/Sources/SwiftSgml/Interfaces/Element+ShortAttributed.swift b/Sources/SwiftSgml/Interfaces/Element+ShortAttributed.swift new file mode 100644 index 0000000..3012cee --- /dev/null +++ b/Sources/SwiftSgml/Interfaces/Element+ShortAttributed.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol ShortAttributedElement: ShortElement & Attributed {} + + +extension ShortAttributedElement { + + public var node: Node { + .short(.init(name: name, attributes: attributes)) + } +} diff --git a/Sources/SwiftSgml/Interfaces/Element+Standard.swift b/Sources/SwiftSgml/Interfaces/Element+Standard.swift new file mode 100644 index 0000000..714bef7 --- /dev/null +++ b/Sources/SwiftSgml/Interfaces/Element+Standard.swift @@ -0,0 +1,42 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol StandardElement: Element, Mutable { + +// associatedtype Child = Ele + + var children: [any Element] { get set } + + func add(child: T) -> Self + func removeChildren() -> Self +} + +extension StandardElement { + + public var node: Node { + .standard(.init(name: name), children.map { $0.node }) + } +} + +public extension StandardElement { + + mutating func removeChildren() { + children.removeAll() + } + + mutating func add(child: T) { + children.append(child) + } + + func add(child: T) -> Self { + mutate { $0.add(child: child) } + } + + func removeChildren() -> Self { + mutate { $0.removeChildren() } + } +} diff --git a/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift b/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift new file mode 100644 index 0000000..d543cda --- /dev/null +++ b/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift @@ -0,0 +1,17 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol StandardAttributedElement: StandardElement & Attributed { + +} + +extension StandardAttributedElement { + + public var node: Node { + .standard(.init(name: name, attributes: attributes), children.map { $0.node }) + } +} diff --git a/Sources/SwiftSgml/Interfaces/Element.swift b/Sources/SwiftSgml/Interfaces/Element.swift new file mode 100644 index 0000000..805c13e --- /dev/null +++ b/Sources/SwiftSgml/Interfaces/Element.swift @@ -0,0 +1,25 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol Element { + var name: String { get } + var node: Node { get } + +// func modify(_ modify: (inout Self) -> Void) -> Self +} + +extension Element { + + public var name: String { + .init(describing: type(of: self)).lowercased() + } +} + + + + + diff --git a/Sources/SwiftSgml/Interfaces/Mutable.swift b/Sources/SwiftSgml/Interfaces/Mutable.swift new file mode 100644 index 0000000..d6596be --- /dev/null +++ b/Sources/SwiftSgml/Interfaces/Mutable.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public protocol Mutable { + func mutate(_ modify: (inout Self) -> Void) -> Self +} + +extension Mutable { + + public func mutate(_ modify: (inout Self) -> Void) -> Self { + var mutableSelf = self + modify(&mutableSelf) + return mutableSelf + } +} diff --git a/Sources/SwiftSgml/Node.swift b/Sources/SwiftSgml/Node.swift deleted file mode 100644 index 2dab1a0..0000000 --- a/Sources/SwiftSgml/Node.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// Node.swift -// SwiftSgml -// -// Created by Tibor Bodecs on 2021. 07. 19.. -// - -public struct Node { - - /// internal node types - public enum NodeType { - /// standard container tags - case standard //
- /// comment tag - case comment // - /// non-container tags - case empty //
- /// invisible node for grouping other nodes - case group // *invisible group*

lorem

ipsum

*invisible group* - } - - public let type: NodeType - public let name: String? - public var contents: String? - public internal(set) var attributes: [Attribute] - - public init(type: NodeType = .standard, - name: String? = nil, - contents: String? = nil, - attributes: [Attribute] = [] - ) { - self.type = type - self.name = name - self.contents = contents - self.attributes = attributes - } -} - -public extension Node { - - /// add or replace an attribute with a given key to the node - mutating func upsert(_ attribute: Attribute) { - delete(attribute) - attributes.append(attribute) - } - - /// deletes a attribute with a given key from the node - mutating func delete(_ attribute: Attribute) { - attributes = attributes.filter { $0.key != attribute.key } - } -} - diff --git a/Sources/SwiftSgml/Node/Node+Comment.swift b/Sources/SwiftSgml/Node/Node+Comment.swift new file mode 100644 index 0000000..81d4ce8 --- /dev/null +++ b/Sources/SwiftSgml/Node/Node+Comment.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct CommentNode { + let value: String + + public init( + value: String + ) { + self.value = value + } +} diff --git a/Sources/SwiftSgml/Node/Node+Short.swift b/Sources/SwiftSgml/Node/Node+Short.swift new file mode 100644 index 0000000..600e3a4 --- /dev/null +++ b/Sources/SwiftSgml/Node/Node+Short.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct ShortNode { + let name: String + let attributes: [any Attribute] + + public init( + name: String, + attributes: [any Attribute] = [] + ) { + self.name = name + self.attributes = attributes + } +} diff --git a/Sources/SwiftSgml/Node/Node+Standard.swift b/Sources/SwiftSgml/Node/Node+Standard.swift new file mode 100644 index 0000000..3072a27 --- /dev/null +++ b/Sources/SwiftSgml/Node/Node+Standard.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct StandardNode { + let name: String + let attributes: [any Attribute] + + public init( + name: String, + attributes: [any Attribute] = [] + ) { + self.name = name + self.attributes = attributes + } +} diff --git a/Sources/SwiftSgml/Node/Node+Text.swift b/Sources/SwiftSgml/Node/Node+Text.swift new file mode 100644 index 0000000..4727870 --- /dev/null +++ b/Sources/SwiftSgml/Node/Node+Text.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct TextNode { + let value: String + + public init( + value: String + ) { + self.value = value + } +} diff --git a/Sources/SwiftSgml/Node/Node.swift b/Sources/SwiftSgml/Node/Node.swift new file mode 100644 index 0000000..d758733 --- /dev/null +++ b/Sources/SwiftSgml/Node/Node.swift @@ -0,0 +1,13 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public enum Node { + case standard(StandardNode, [Node]) + case short(ShortNode) + case text(TextNode) + case comment(CommentNode) +} diff --git a/Sources/SwiftSgml/Tag.swift b/Sources/SwiftSgml/Tag.swift deleted file mode 100644 index 530fcde..0000000 --- a/Sources/SwiftSgml/Tag.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// Tag.swift -// SwiftSgml -// -// Created by Tibor Bodecs on 2021. 11. 19.. -// - -open class Tag { - - public private(set) var node: Node - public private(set) var children: [Tag] - - // MARK: - init - - open class func createNode() -> Node { - Node(type: .standard, name: String(describing: self).lowercased()) - } - - /// initialize a new Tag with child tags - public init(_ children: [Tag] = []) { - self.node = Self.createNode() - self.children = children - } - - /// initialize a new Tag with a single child tag - public convenience init(_ child: Tag) { - self.init([child]) - } - - /// initialize a new Tag with children using a builder - public convenience init(@TagBuilder _ builder: () -> Tag) { - self.init([builder()]) - } - -// /// initialize a new Tag with children using an async throwing builder -// public convenience init(@TagBuilder _ builder: () async throws -> [Tag]) async throws { -// self.init(try await builder()) -// } - - - /// initialize a new Tag with some contents - public convenience init(_ contents: String?) { - self.init() - if let contents = contents { - setContents(contents) - } - } - - // MARK: - contents - - /// set contents - @discardableResult - public func setContents(_ value: String?, _ condition: Bool = true) -> Self { - if condition { - node.contents = value - } - return self - } - - // MARK: - attributes - - /// set attributes - @discardableResult - public func setAttributes(_ attributes: [Attribute], _ condition: Bool = true) -> Self { - if condition { - node.attributes = attributes - } - return self - } - - /// delete an attribute by a given key - @discardableResult - public func deleteAttribute(_ key: String, _ condition: Bool = true) -> Self { - if condition { - node.delete(Attribute(key: key)) - } - return self - } - - /// add a new attribute with a given value if the condition is true - @discardableResult - public func attribute(_ key: String, _ value: String?, _ condition: Bool = true) -> Self { - if let value = value, condition { - node.upsert(Attribute(key: key, value: value)) - } - return self - } - - /// add a new flag-like attribute with a given value if the condition is true - /// - /// if the flag value is `nil` only the attribute key will be used. eg. `` - /// - @discardableResult - public func flagAttribute(_ key: String, _ value: String? = nil, _ condition: Bool = true) -> Self { - if condition { - node.upsert(Attribute(key: key, value: value)) - } - return self - } -} diff --git a/Sources/SwiftSgml/TagBuilder.swift b/Sources/SwiftSgml/TagBuilder.swift deleted file mode 100644 index 704a3a2..0000000 --- a/Sources/SwiftSgml/TagBuilder.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// TagBuilder.swift -// SwiftSgml -// -// Created by Tibor Bodecs on 2021. 07. 19.. -// - -@resultBuilder -public enum TagBuilder { - - public static func buildBlock(_ components: [Tag]...) -> Tag { - GroupTag(components.flatMap { $0 }) - } - - public static func buildBlock(_ components: Tag...) -> Tag { - GroupTag(components) - } - - public static func buildOptional(_ component: Tag?) -> Tag { - component ?? GroupTag() - } - - public static func buildEither(first component: Tag) -> Tag { - component - } - - public static func buildEither(second component: Tag) -> Tag { - component - } - - public static func buildArray(_ components: [Tag]) -> Tag { - GroupTag(components) - } -} - diff --git a/Tests/SwiftSgmlTests/AttributeTests.swift b/Tests/SwiftSgmlTests/AttributeTests.swift index e433909..e772411 100644 --- a/Tests/SwiftSgmlTests/AttributeTests.swift +++ b/Tests/SwiftSgmlTests/AttributeTests.swift @@ -10,6 +10,60 @@ import XCTest final class AttributeTests: XCTestCase { + func testCustomAttribute() { + let document = Document(.xml) { + Root() + .add(attribute: Custom(key: "foo", value: "bar")) + } + + let expectation = """ + + + """ + XCTAssertDocument(document, expectation) + } + + func testCustomNilAttribute() { + let document = Document(.xml) { + Root() + .add(attribute: Custom(key: "foo")) + } + + let expectation = """ + + + """ + XCTAssertDocument(document, expectation) + } + + func testCustomEmptyAttribute() { + let document = Document(.xml) { + Root() + .add(attribute: Custom(key: "foo", value: "")) + } + + let expectation = """ + + + """ + XCTAssertDocument(document, expectation) + } + + func testFlagAttribute() { + let document = Document() { + Root { + Custom(key: "foo", value: nil) + } + } + + let expectation = """ + + """ + XCTAssertDocument(document, expectation) + } + + // MARK: - + // func testSetAttributes() { // // let doc = Document { diff --git a/Tests/SwiftSgmlTests/CustomTags.swift b/Tests/SwiftSgmlTests/CustomTags.swift deleted file mode 100644 index 5b0a5c0..0000000 --- a/Tests/SwiftSgmlTests/CustomTags.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 2021. 12. 19.. -// - -import SwiftSgml - -final class Root: Tag { - -} - -final class Leaf: Tag { - -} - -final class Branch: Tag { - -} diff --git a/Tests/SwiftSgmlTests/ElementBuilderTests.swift b/Tests/SwiftSgmlTests/ElementBuilderTests.swift new file mode 100644 index 0000000..b3e83d9 --- /dev/null +++ b/Tests/SwiftSgmlTests/ElementBuilderTests.swift @@ -0,0 +1,202 @@ +// +// TagBuilderTests.swift +// SwiftHtmlTests +// +// Created by Tibor Bodecs on 2021. 07. 11.. +// + +import XCTest +@testable import SwiftSgml + +final class TagBuilderTests: XCTestCase { + + func testOptionalBuilder() { + let condition: Bool = false + let document = Document { + Root { +// if condition { +// Leaf("a") +// } + Branch { + Leaf("b") + } + } + } + + let expectation = """ + + + b + + + """ + + XCTAssertDocument(document, expectation) + } +// +// func testEitherFirstBuilder() { +// let condition: Bool = true +// let doc = Document { +// Branch { +// if condition { +// Leaf("a") +// } +// else { +// Leaf("b") +// } +// Leaf("c") +// } +// } +// +// let html = """ +// +// a +// c +// +// """ +// +// assert(doc: doc, html: html) +// } +// +// func testEitherSecondBuilder() { +// let condition: Bool = false +// let doc = Document { +// Branch { +// if condition { +// Leaf("a") +// } +// else { +// Leaf("b") +// } +// Leaf("c") +// } +// } +// +// let html = """ +// +// b +// c +// +// """ +// +// assert(doc: doc, html: html) +// } +// +// func testArrayBuilder() { +// let doc = Document { +// Branch { +// for i in 0..<3 { +// Leaf(String(i + 1)) +// } +// } +// } +// +// let html = """ +// +// 1 +// 2 +// 3 +// +// """ +// assert(doc: doc, html: html) +// } +// +// func testGroupBuilder() { +// let doc = Document { +// Branch { +// Leaf("foo") +// Leaf("bar") +// } +// } +// +// let html = """ +// +// foo +// bar +// +// """ +// assert(doc: doc, html: html) +// } +// +// func testSingleGroupBuilder() { +// let doc = Document { +// Branch { +// [ +// Leaf("Lorem") +// ] +// } +// } +// +// let html = """ +// +// Lorem +// +// """ +// assert(doc: doc, html: html) +// } +// +// func testMultiGroupBuilder() { +// let doc = Document { +// Branch { +// [ +// Leaf("Lorem"), +// Leaf("Dolor"), +// ] +// } +// } +// +// let html = """ +// +// Lorem +// Dolor +// +// """ +// assert(doc: doc, html: html) +// } +// +// func testGroupTagBuilderAndRenderer() { +// let doc = Document { +// Branch { +// GroupTag { +// Leaf("a") +// Leaf("b") +// } +// } +// } +// +// let html = """ +// +// a +// b +// +// """ +// assert(doc: doc, html: html) +// } +// +// func testMultiGroupTagBuilderAndRenderer() { +// let values: [String] = ["a", "b", "c"] +// +// let doc = Document { +// Branch { +// values.map { item -> Tag in +// GroupTag { +// Leaf(item) +// Leaf(item) +// } +// } +// } +// } +// +// let html = """ +// +// a +// a +// b +// b +// c +// c +// +// """ +// assert(doc: doc, html: html) +// } +} diff --git a/Tests/SwiftSgmlTests/ElementTests.swift b/Tests/SwiftSgmlTests/ElementTests.swift new file mode 100644 index 0000000..8ef3945 --- /dev/null +++ b/Tests/SwiftSgmlTests/ElementTests.swift @@ -0,0 +1,80 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 2022. 02. 24.. +// + +import XCTest +import SwiftSgml + +final class TagTests: XCTestCase { + + func testBasicStructure() { + let doc = Document { + Root { + Branch { + Leaf("hello") + } + } + } + + let expectation = """ + + + hello + + + """ + + let renderer = DocumentRenderer() + let result = renderer.render(doc) + + XCTAssertEqual(expectation.minify(), result) + } + + func testAddChildrenStructure() { + let doc = Document { + Root() + .add(child: Branch()) + .add(child: Leaf("asdf")) // TODO: this shouldn't be possible +// .add(child: Leaf("foo")) +// .add(child: Leaf("bar")) + } + + let expectation = """ + + + foo + bar + + + """ + + let renderer = DocumentRenderer() + let result = renderer.render(doc) + + XCTAssertEqual(expectation.minify(), result) + } + + func testRemoveAllChildrenStructure() { + let doc = Document { + Root { + Branch { + Leaf("hello") + Leaf("world") + } + } + .removeChildren() + } + + let expectation = """ + + """ + + let renderer = DocumentRenderer() + let result = renderer.render(doc) + + XCTAssertEqual(expectation.minify(), result) + } +} diff --git a/Tests/SwiftSgmlTests/NodeTests.swift b/Tests/SwiftSgmlTests/NodeTests.swift deleted file mode 100644 index c47c92d..0000000 --- a/Tests/SwiftSgmlTests/NodeTests.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 2022. 02. 06.. -// - -import XCTest -@testable import SwiftSgml - -final class NodeTests: XCTestCase { - -// func testNodeContents() { -// final class Foo: Tag { -// -// } -// -// let doc = Document { -// Foo("bar") -// .setContents("baz") -// } -// -// let html = """ -// baz -// """ -// } -} diff --git a/Tests/SwiftSgmlTests/SwiftSgmlTests.swift b/Tests/SwiftSgmlTests/SwiftSgmlTests.swift index 83c4d73..5591809 100644 --- a/Tests/SwiftSgmlTests/SwiftSgmlTests.swift +++ b/Tests/SwiftSgmlTests/SwiftSgmlTests.swift @@ -11,52 +11,16 @@ import XCTest final class SwiftSgmlTests: XCTestCase { func testXmlDocument() { - let doc = Document(.xml) { + let document = Document(.xml) { Root() } - let html = """ + let expectation = """ """ - assert(doc: doc, html: html) + XCTAssertDocument(document, expectation) } - func testCustomAttribute() { - let doc = Document(.xml) { - Root() - .attribute("foo", "bar") - } - - let html = """ - - - """ - assert(doc: doc, html: html) - } - - func testCustomNilAttribute() { - let doc = Document(.xml) { - Root() - .attribute("foo", nil) - } - - let html = """ - - - """ - assert(doc: doc, html: html) - } - - func testFlagAttribute() { - let doc = Document() { - Root() - .flagAttribute("foo") - } - - let html = """ - - """ - assert(doc: doc, html: html) - } + } diff --git a/Tests/SwiftSgmlTests/TagBuilderTests.swift b/Tests/SwiftSgmlTests/TagBuilderTests.swift deleted file mode 100644 index e17fc31..0000000 --- a/Tests/SwiftSgmlTests/TagBuilderTests.swift +++ /dev/null @@ -1,199 +0,0 @@ -// -// TagBuilderTests.swift -// SwiftHtmlTests -// -// Created by Tibor Bodecs on 2021. 07. 11.. -// - -import XCTest -@testable import SwiftSgml - -final class TagBuilderTests: XCTestCase { - - func testOptionalBuilder() { - let condition: Bool = false - let doc = Document { - Branch { - if condition { - Leaf("a") - } - Leaf("b") - } - } - - let html = """ - - b - - """ - - assert(doc: doc, html: html) - } - - func testEitherFirstBuilder() { - let condition: Bool = true - let doc = Document { - Branch { - if condition { - Leaf("a") - } - else { - Leaf("b") - } - Leaf("c") - } - } - - let html = """ - - a - c - - """ - - assert(doc: doc, html: html) - } - - func testEitherSecondBuilder() { - let condition: Bool = false - let doc = Document { - Branch { - if condition { - Leaf("a") - } - else { - Leaf("b") - } - Leaf("c") - } - } - - let html = """ - - b - c - - """ - - assert(doc: doc, html: html) - } - - func testArrayBuilder() { - let doc = Document { - Branch { - for i in 0..<3 { - Leaf(String(i + 1)) - } - } - } - - let html = """ - - 1 - 2 - 3 - - """ - assert(doc: doc, html: html) - } - - func testGroupBuilder() { - let doc = Document { - Branch { - Leaf("foo") - Leaf("bar") - } - } - - let html = """ - - foo - bar - - """ - assert(doc: doc, html: html) - } - - func testSingleGroupBuilder() { - let doc = Document { - Branch { - [ - Leaf("Lorem") - ] - } - } - - let html = """ - - Lorem - - """ - assert(doc: doc, html: html) - } - - func testMultiGroupBuilder() { - let doc = Document { - Branch { - [ - Leaf("Lorem"), - Leaf("Dolor"), - ] - } - } - - let html = """ - - Lorem - Dolor - - """ - assert(doc: doc, html: html) - } - - func testGroupTagBuilderAndRenderer() { - let doc = Document { - Branch { - GroupTag { - Leaf("a") - Leaf("b") - } - } - } - - let html = """ - - a - b - - """ - assert(doc: doc, html: html) - } - - func testMultiGroupTagBuilderAndRenderer() { - let values: [String] = ["a", "b", "c"] - - let doc = Document { - Branch { - values.map { item -> Tag in - GroupTag { - Leaf(item) - Leaf(item) - } - } - } - } - - let html = """ - - a - a - b - b - c - c - - """ - assert(doc: doc, html: html) - } -} - diff --git a/Tests/SwiftSgmlTests/TagTests.swift b/Tests/SwiftSgmlTests/TagTests.swift deleted file mode 100644 index 1f4f389..0000000 --- a/Tests/SwiftSgmlTests/TagTests.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 2022. 02. 24.. -// - -import XCTest -@testable import SwiftSgml - -final class TagTests: XCTestCase { - - func testConvenienceSingleTagInit() { - - let doc = Document { - Root(Leaf("hello")) - } - - let html = """ - - hello - - """ - assert(doc: doc, html: html) - } -} diff --git a/Tests/SwiftSgmlTests/Utils/Branch.swift b/Tests/SwiftSgmlTests/Utils/Branch.swift new file mode 100644 index 0000000..205727f --- /dev/null +++ b/Tests/SwiftSgmlTests/Utils/Branch.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import SwiftSgml + +struct Branch: StandardElement { + + var children: [any Element] + + public init( + @Builder elements b1: () -> [Element] = { [] } + ) { + self.children = b1() + } +} diff --git a/Tests/SwiftSgmlTests/Utils/Leaf.swift b/Tests/SwiftSgmlTests/Utils/Leaf.swift new file mode 100644 index 0000000..e3ce0f7 --- /dev/null +++ b/Tests/SwiftSgmlTests/Utils/Leaf.swift @@ -0,0 +1,21 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import SwiftSgml + +struct Leaf: StandardElement { + + var children: [any Element] + + init(_ value: String) { + self.children = [ + Text(value) + ] + } + + +} diff --git a/Tests/SwiftSgmlTests/Utils/Root.swift b/Tests/SwiftSgmlTests/Utils/Root.swift new file mode 100644 index 0000000..12f0670 --- /dev/null +++ b/Tests/SwiftSgmlTests/Utils/Root.swift @@ -0,0 +1,36 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import SwiftSgml + +protocol RootChildElement: Element {} + +extension Branch: RootChildElement {} + +struct Root: StandardAttributedElement { + + var children: [any Element] + var attributes: [any Attribute] + + init( + @Builder attributes b2: () -> [any Attribute] = { [] }, + @Builder elements b1: () -> [RootChildElement] = { [] } + ) { + self.children = b1() + self.attributes = b2() + } + + init( + @Builder _ b1: () -> [RootChildElement] = { [] } + ) { + self.init(attributes: {}, elements: b1) + } + +} + + + diff --git a/Tests/SwiftSgmlTests/Utils/String+Minify.swift b/Tests/SwiftSgmlTests/Utils/String+Minify.swift new file mode 100644 index 0000000..ffacc43 --- /dev/null +++ b/Tests/SwiftSgmlTests/Utils/String+Minify.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +extension String { + + func minify() -> String { + self + .replacingOccurrences(of: " ", with: "") + .replacingOccurrences(of: "\n", with: "") + } +} diff --git a/Tests/SwiftSgmlTests/Utils/XCTAssertDocument.swift b/Tests/SwiftSgmlTests/Utils/XCTAssertDocument.swift new file mode 100644 index 0000000..009098d --- /dev/null +++ b/Tests/SwiftSgmlTests/Utils/XCTAssertDocument.swift @@ -0,0 +1,20 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import XCTest +import SwiftSgml + +func XCTAssertDocument( + _ document: Document, + _ expectation: String, + file: StaticString = #filePath, + line: UInt = #line +) { + let renderer = DocumentRenderer() + let html = renderer.render(document) + XCTAssertEqual(html, expectation.minify(), file: file, line: line) +} diff --git a/Tests/SwiftSgmlTests/assert.swift b/Tests/SwiftSgmlTests/assert.swift deleted file mode 100644 index fcb75c4..0000000 --- a/Tests/SwiftSgmlTests/assert.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// assert.swift -// SwiftSgmlTests -// -// Created by Tibor Bodecs on 2023. 04. 01.. -// - -import SwiftSgml -import XCTest - -func assert(doc: Document, html: String) { - let renderer = DocumentRenderer(minify: true) - let expectation = html - .replacingOccurrences(of: " ", with: "") - .replacingOccurrences(of: "\n", with: "") - XCTAssertEqual(renderer.render(doc), expectation) -} From aa151750c35d71407c1336d3b32836b7c0bcaef9 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 16:05:25 +0100 Subject: [PATCH 02/48] better protocols --- Sources/SwiftSgml/Interfaces/Attributed.swift | 19 ++++++++++--------- .../Interfaces/Element+Standard.swift | 11 +++-------- .../Element+StandardAttributed.swift | 6 +----- Sources/SwiftSgml/Interfaces/Mutable.swift | 2 +- Tests/SwiftSgmlTests/ElementTests.swift | 13 ++++++++----- Tests/SwiftSgmlTests/Utils/Branch.swift | 2 +- Tests/SwiftSgmlTests/Utils/Root.swift | 5 ++++- 7 files changed, 28 insertions(+), 30 deletions(-) diff --git a/Sources/SwiftSgml/Interfaces/Attributed.swift b/Sources/SwiftSgml/Interfaces/Attributed.swift index ccfff29..347168d 100644 --- a/Sources/SwiftSgml/Interfaces/Attributed.swift +++ b/Sources/SwiftSgml/Interfaces/Attributed.swift @@ -5,17 +5,18 @@ // Created by Tibor Bodecs on 24/02/2024. // -public protocol Attributed: Mutable { - - var attributes: [any Attribute] { get set } +public protocol CustomAttributed { + associatedtype A: Attribute + + var attributes: [A] { get set } +} -// mutating func addAttribute(_ attribute: Attribute) +public protocol Attributed { - func add(attribute: any Attribute) -> Self - func remove(attributeByKey: String) -> Self + var attributes: [any Attribute] { get set } } -extension Attributed { +public extension Attributed where Self: Mutable { func index(of attribute: any Attribute) -> [any Attribute].Index? { attributes.firstIndex(where: { $0.key == attribute.key }) @@ -36,11 +37,11 @@ extension Attributed { // MARK: - - public func add(attribute: any Attribute) -> Self { + func add(attribute: any Attribute) -> Self { mutate { $0.add(attribute: attribute) } } - public func remove(attributeByKey key: String) -> Self { + func remove(attributeByKey key: String) -> Self { mutate { $0.remove(attributeByKey: key) } } } diff --git a/Sources/SwiftSgml/Interfaces/Element+Standard.swift b/Sources/SwiftSgml/Interfaces/Element+Standard.swift index 714bef7..d65672c 100644 --- a/Sources/SwiftSgml/Interfaces/Element+Standard.swift +++ b/Sources/SwiftSgml/Interfaces/Element+Standard.swift @@ -5,24 +5,19 @@ // Created by Tibor Bodecs on 24/02/2024. // -public protocol StandardElement: Element, Mutable { - -// associatedtype Child = Ele +public protocol StandardElement: Element { var children: [any Element] { get set } - - func add(child: T) -> Self - func removeChildren() -> Self } extension StandardElement { - + public var node: Node { .standard(.init(name: name), children.map { $0.node }) } } -public extension StandardElement { +public extension StandardElement where Self: Mutable { mutating func removeChildren() { children.removeAll() diff --git a/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift b/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift index d543cda..e54c497 100644 --- a/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift +++ b/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift @@ -5,11 +5,7 @@ // Created by Tibor Bodecs on 24/02/2024. // -public protocol StandardAttributedElement: StandardElement & Attributed { - -} - -extension StandardAttributedElement { +extension StandardElement where Self: Attributed { public var node: Node { .standard(.init(name: name, attributes: attributes), children.map { $0.node }) diff --git a/Sources/SwiftSgml/Interfaces/Mutable.swift b/Sources/SwiftSgml/Interfaces/Mutable.swift index d6596be..78b3fc8 100644 --- a/Sources/SwiftSgml/Interfaces/Mutable.swift +++ b/Sources/SwiftSgml/Interfaces/Mutable.swift @@ -8,7 +8,7 @@ public protocol Mutable { func mutate(_ modify: (inout Self) -> Void) -> Self } - + extension Mutable { public func mutate(_ modify: (inout Self) -> Void) -> Self { diff --git a/Tests/SwiftSgmlTests/ElementTests.swift b/Tests/SwiftSgmlTests/ElementTests.swift index 8ef3945..743d40b 100644 --- a/Tests/SwiftSgmlTests/ElementTests.swift +++ b/Tests/SwiftSgmlTests/ElementTests.swift @@ -35,11 +35,14 @@ final class TagTests: XCTestCase { func testAddChildrenStructure() { let doc = Document { - Root() - .add(child: Branch()) - .add(child: Leaf("asdf")) // TODO: this shouldn't be possible -// .add(child: Leaf("foo")) -// .add(child: Leaf("bar")) + Root { + Branch() + .add(child: Leaf("foo")) + .add(child: Leaf("bar")) + } +// .add(child: Branch()) +// .add(child: Leaf("asdf")) // TODO: this shouldn't be possible + } let expectation = """ diff --git a/Tests/SwiftSgmlTests/Utils/Branch.swift b/Tests/SwiftSgmlTests/Utils/Branch.swift index 205727f..1636712 100644 --- a/Tests/SwiftSgmlTests/Utils/Branch.swift +++ b/Tests/SwiftSgmlTests/Utils/Branch.swift @@ -7,7 +7,7 @@ import SwiftSgml -struct Branch: StandardElement { +struct Branch: StandardElement, Mutable { var children: [any Element] diff --git a/Tests/SwiftSgmlTests/Utils/Root.swift b/Tests/SwiftSgmlTests/Utils/Root.swift index 12f0670..3fbfb71 100644 --- a/Tests/SwiftSgmlTests/Utils/Root.swift +++ b/Tests/SwiftSgmlTests/Utils/Root.swift @@ -11,7 +11,7 @@ protocol RootChildElement: Element {} extension Branch: RootChildElement {} -struct Root: StandardAttributedElement { +struct Root: StandardElement, Attributed, Mutable { var children: [any Element] var attributes: [any Attribute] @@ -30,6 +30,9 @@ struct Root: StandardAttributedElement { self.init(attributes: {}, elements: b1) } +// func add(child: RootChildElement) -> Self { +// mutate { $0.children.append(child) } +// } } From 71712571221829f932fc6f13d63f1fbf8e71c152 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 16:13:33 +0100 Subject: [PATCH 03/48] experiment --- .../{Attributed.swift => Attributes.swift} | 13 ++++++++++--- .../SwiftSgml/Interfaces/Element+Short.swift | 8 ++++++++ .../Interfaces/Element+ShortAttributed.swift | 16 ---------------- .../Interfaces/Element+Standard.swift | 18 +++++++++++++++--- .../Element+StandardAttributed.swift | 13 ------------- Sources/SwiftSgml/Interfaces/Element.swift | 2 -- Tests/SwiftSgmlTests/Utils/Branch.swift | 2 +- Tests/SwiftSgmlTests/Utils/Leaf.swift | 2 +- Tests/SwiftSgmlTests/Utils/Root.swift | 12 ++++++++---- 9 files changed, 43 insertions(+), 43 deletions(-) rename Sources/SwiftSgml/Interfaces/{Attributed.swift => Attributes.swift} (74%) delete mode 100644 Sources/SwiftSgml/Interfaces/Element+ShortAttributed.swift delete mode 100644 Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift diff --git a/Sources/SwiftSgml/Interfaces/Attributed.swift b/Sources/SwiftSgml/Interfaces/Attributes.swift similarity index 74% rename from Sources/SwiftSgml/Interfaces/Attributed.swift rename to Sources/SwiftSgml/Interfaces/Attributes.swift index 347168d..219282d 100644 --- a/Sources/SwiftSgml/Interfaces/Attributed.swift +++ b/Sources/SwiftSgml/Interfaces/Attributes.swift @@ -5,18 +5,25 @@ // Created by Tibor Bodecs on 24/02/2024. // -public protocol CustomAttributed { +public protocol CustomAttributes { associatedtype A: Attribute var attributes: [A] { get set } } -public protocol Attributed { +public protocol Attributes { var attributes: [any Attribute] { get set } } -public extension Attributed where Self: Mutable { +public protocol MutableAttributes: Attributes, Mutable { + + func index(of attribute: any Attribute) -> [any Attribute].Index? + func add(attribute: any Attribute) -> Self + func remove(attributeByKey key: String) -> Self +} + +public extension MutableAttributes { func index(of attribute: any Attribute) -> [any Attribute].Index? { attributes.firstIndex(where: { $0.key == attribute.key }) diff --git a/Sources/SwiftSgml/Interfaces/Element+Short.swift b/Sources/SwiftSgml/Interfaces/Element+Short.swift index 9347f40..725ba8a 100644 --- a/Sources/SwiftSgml/Interfaces/Element+Short.swift +++ b/Sources/SwiftSgml/Interfaces/Element+Short.swift @@ -13,3 +13,11 @@ extension ShortElement { .short(.init(name: name)) } } + +extension ShortElement where Self: Attributes { + + public var node: Node { + .short(.init(name: name, attributes: attributes)) + } +} + diff --git a/Sources/SwiftSgml/Interfaces/Element+ShortAttributed.swift b/Sources/SwiftSgml/Interfaces/Element+ShortAttributed.swift deleted file mode 100644 index 3012cee..0000000 --- a/Sources/SwiftSgml/Interfaces/Element+ShortAttributed.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 24/02/2024. -// - -public protocol ShortAttributedElement: ShortElement & Attributed {} - - -extension ShortAttributedElement { - - public var node: Node { - .short(.init(name: name, attributes: attributes)) - } -} diff --git a/Sources/SwiftSgml/Interfaces/Element+Standard.swift b/Sources/SwiftSgml/Interfaces/Element+Standard.swift index d65672c..a33c7d5 100644 --- a/Sources/SwiftSgml/Interfaces/Element+Standard.swift +++ b/Sources/SwiftSgml/Interfaces/Element+Standard.swift @@ -5,19 +5,31 @@ // Created by Tibor Bodecs on 24/02/2024. // -public protocol StandardElement: Element { +public protocol ParentElement: Element { var children: [any Element] { get set } } -extension StandardElement { +extension ParentElement { public var node: Node { .standard(.init(name: name), children.map { $0.node }) } } -public extension StandardElement where Self: Mutable { +extension ParentElement where Self: Attributes { + + public var node: Node { + .standard(.init(name: name, attributes: attributes), children.map { $0.node }) + } +} + +public protocol MutableParentElement: ParentElement, Mutable { + func add(child: T) -> Self + func removeChildren() -> Self +} + +public extension MutableParentElement { mutating func removeChildren() { children.removeAll() diff --git a/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift b/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift deleted file mode 100644 index e54c497..0000000 --- a/Sources/SwiftSgml/Interfaces/Element+StandardAttributed.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// File.swift -// -// -// Created by Tibor Bodecs on 24/02/2024. -// - -extension StandardElement where Self: Attributed { - - public var node: Node { - .standard(.init(name: name, attributes: attributes), children.map { $0.node }) - } -} diff --git a/Sources/SwiftSgml/Interfaces/Element.swift b/Sources/SwiftSgml/Interfaces/Element.swift index 805c13e..c687f07 100644 --- a/Sources/SwiftSgml/Interfaces/Element.swift +++ b/Sources/SwiftSgml/Interfaces/Element.swift @@ -8,8 +8,6 @@ public protocol Element { var name: String { get } var node: Node { get } - -// func modify(_ modify: (inout Self) -> Void) -> Self } extension Element { diff --git a/Tests/SwiftSgmlTests/Utils/Branch.swift b/Tests/SwiftSgmlTests/Utils/Branch.swift index 1636712..ac42f9a 100644 --- a/Tests/SwiftSgmlTests/Utils/Branch.swift +++ b/Tests/SwiftSgmlTests/Utils/Branch.swift @@ -7,7 +7,7 @@ import SwiftSgml -struct Branch: StandardElement, Mutable { +struct Branch: MutableParentElement { var children: [any Element] diff --git a/Tests/SwiftSgmlTests/Utils/Leaf.swift b/Tests/SwiftSgmlTests/Utils/Leaf.swift index e3ce0f7..359eba1 100644 --- a/Tests/SwiftSgmlTests/Utils/Leaf.swift +++ b/Tests/SwiftSgmlTests/Utils/Leaf.swift @@ -7,7 +7,7 @@ import SwiftSgml -struct Leaf: StandardElement { +struct Leaf: ParentElement { var children: [any Element] diff --git a/Tests/SwiftSgmlTests/Utils/Root.swift b/Tests/SwiftSgmlTests/Utils/Root.swift index 3fbfb71..3972ddc 100644 --- a/Tests/SwiftSgmlTests/Utils/Root.swift +++ b/Tests/SwiftSgmlTests/Utils/Root.swift @@ -11,7 +11,7 @@ protocol RootChildElement: Element {} extension Branch: RootChildElement {} -struct Root: StandardElement, Attributed, Mutable { +struct Root: ParentElement, MutableAttributes { var children: [any Element] var attributes: [any Attribute] @@ -30,9 +30,13 @@ struct Root: StandardElement, Attributed, Mutable { self.init(attributes: {}, elements: b1) } -// func add(child: RootChildElement) -> Self { -// mutate { $0.children.append(child) } -// } + func add(child: RootChildElement) -> Self { + mutate { $0.children.append(child) } + } + + func removeChildren() -> Self { + mutate { $0.children.removeAll() } + } } From ecdbe985a0f2c2a3e8829bb6560b72300ba00653 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 16:23:54 +0100 Subject: [PATCH 04/48] protocol wip --- Sources/SwiftSgml/Interfaces/Attributes.swift | 2 +- Tests/SwiftSgmlTests/ElementTests.swift | 5 +--- Tests/SwiftSgmlTests/Utils/Root.swift | 29 +++++++++++++++---- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Sources/SwiftSgml/Interfaces/Attributes.swift b/Sources/SwiftSgml/Interfaces/Attributes.swift index 219282d..ba4b0e1 100644 --- a/Sources/SwiftSgml/Interfaces/Attributes.swift +++ b/Sources/SwiftSgml/Interfaces/Attributes.swift @@ -8,7 +8,7 @@ public protocol CustomAttributes { associatedtype A: Attribute - var attributes: [A] { get set } + var attributes: A { get set } } public protocol Attributes { diff --git a/Tests/SwiftSgmlTests/ElementTests.swift b/Tests/SwiftSgmlTests/ElementTests.swift index 743d40b..f5ed5d9 100644 --- a/Tests/SwiftSgmlTests/ElementTests.swift +++ b/Tests/SwiftSgmlTests/ElementTests.swift @@ -39,10 +39,7 @@ final class TagTests: XCTestCase { Branch() .add(child: Leaf("foo")) .add(child: Leaf("bar")) - } -// .add(child: Branch()) -// .add(child: Leaf("asdf")) // TODO: this shouldn't be possible - + } } let expectation = """ diff --git a/Tests/SwiftSgmlTests/Utils/Root.swift b/Tests/SwiftSgmlTests/Utils/Root.swift index 3972ddc..c333bd2 100644 --- a/Tests/SwiftSgmlTests/Utils/Root.swift +++ b/Tests/SwiftSgmlTests/Utils/Root.swift @@ -11,13 +11,21 @@ protocol RootChildElement: Element {} extension Branch: RootChildElement {} -struct Root: ParentElement, MutableAttributes { +protocol RootAttribute: Attribute {} - var children: [any Element] - var attributes: [any Attribute] +extension Custom: RootAttribute {} + +struct Root: Element, Mutable { + + var children: [any RootChildElement] + var attributes: [any RootAttribute] + + var node: Node { + .standard(.init(name: name, attributes: attributes), children.map { $0.node }) + } init( - @Builder attributes b2: () -> [any Attribute] = { [] }, + @Builder attributes b2: () -> [RootAttribute] = { [] }, @Builder elements b1: () -> [RootChildElement] = { [] } ) { self.children = b1() @@ -30,6 +38,8 @@ struct Root: ParentElement, MutableAttributes { self.init(attributes: {}, elements: b1) } + // MARK: - + func add(child: RootChildElement) -> Self { mutate { $0.children.append(child) } } @@ -37,7 +47,14 @@ struct Root: ParentElement, MutableAttributes { func removeChildren() -> Self { mutate { $0.children.removeAll() } } -} - + + // MARK: - + func add(attribute: RootAttribute) -> Self { + mutate { $0.attributes.append(attribute) } + } + func removeAttributes() -> Self { + mutate { $0.attributes.removeAll() } + } +} From 13bb6e34f3345286b7bff06c64c709cad45a6394 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 20:02:40 +0100 Subject: [PATCH 05/48] element builder --- Sources/SwiftHtml/Text.swift | 18 ++--- Sources/SwiftSgml/Attributes/Custom.swift | 18 ++--- Sources/SwiftSgml/Attributes/Flag.swift | 19 ++++++ Sources/SwiftSgml/Builder.swift | 65 +++++++++++-------- .../Document/Document+Renderer.swift | 6 +- Sources/SwiftSgml/Interfaces/Attribute.swift | 7 -- Sources/SwiftSgml/Interfaces/Attributes.swift | 8 +-- ...nt+Standard.swift => Element+Parent.swift} | 8 +-- Sources/SwiftSgml/Interfaces/Element.swift | 5 +- Sources/SwiftSgml/Interfaces/Mutable.swift | 2 +- Sources/SwiftSgml/Node/Node+Comment.swift | 2 +- Sources/SwiftSgml/Node/Node+Short.swift | 2 +- Sources/SwiftSgml/Node/Node+Standard.swift | 2 +- Sources/SwiftSgml/Node/Node+Text.swift | 2 +- Sources/SwiftSgml/Node/Node.swift | 2 +- .../SwiftSgmlTests/ElementBuilderTests.swift | 6 +- Tests/SwiftSgmlTests/Utils/Root.swift | 4 +- .../SwiftSgmlTests/Utils}/Text.swift | 8 ++- 18 files changed, 98 insertions(+), 86 deletions(-) create mode 100644 Sources/SwiftSgml/Attributes/Flag.swift rename Sources/SwiftSgml/Interfaces/{Element+Standard.swift => Element+Parent.swift} (84%) rename {Sources/SwiftSgml/Elements => Tests/SwiftSgmlTests/Utils}/Text.swift (53%) diff --git a/Sources/SwiftHtml/Text.swift b/Sources/SwiftHtml/Text.swift index 0745742..8d33be7 100644 --- a/Sources/SwiftHtml/Text.swift +++ b/Sources/SwiftHtml/Text.swift @@ -1,15 +1,17 @@ // -// Text.swift -// SwiftHtml +// File.swift +// // -// Created by Tibor Bodecs on 2021. 11. 29.. +// Created by Tibor Bodecs on 24/02/2024. // -/// a plain text node to write simple textual content into the html tree -open class Text: GroupTag { +public struct Text: Element { - public init(_ contents: String) { - super.init() - setContents(contents) + let value: String + + public init(_ value: String) { + self.value = value } + + public var node: Node { .text(.init(value: value)) } } diff --git a/Sources/SwiftSgml/Attributes/Custom.swift b/Sources/SwiftSgml/Attributes/Custom.swift index 602d9b4..13958d2 100644 --- a/Sources/SwiftSgml/Attributes/Custom.swift +++ b/Sources/SwiftSgml/Attributes/Custom.swift @@ -6,24 +6,16 @@ // public struct Custom: Attribute { + public let key: String public var value: String? - public init(key: String, value: String? = nil) { + public init( + key: String, + value: String? = nil + ) { self.key = key self.value = value } } -public struct Flag: Attribute { - - public let key: String - public var value: String? { - get { nil } - set {} - } - - public init(_ key: String) { - self.key = key - } -} diff --git a/Sources/SwiftSgml/Attributes/Flag.swift b/Sources/SwiftSgml/Attributes/Flag.swift new file mode 100644 index 0000000..3ca0543 --- /dev/null +++ b/Sources/SwiftSgml/Attributes/Flag.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +public struct Flag: Attribute { + + public let key: String + public var value: String? { + get { nil } + set {} + } + + public init(_ key: String) { + self.key = key + } +} diff --git a/Sources/SwiftSgml/Builder.swift b/Sources/SwiftSgml/Builder.swift index a7449d5..8884a1f 100644 --- a/Sources/SwiftSgml/Builder.swift +++ b/Sources/SwiftSgml/Builder.swift @@ -11,33 +11,42 @@ public enum Builder { public static func buildBlock(_ components: T...) -> [T] { components } + + public static func buildEither(first component: T) -> T { + component + } + + public static func buildEither(second component: T) -> T { + component + } } -//@resultBuilder -//public enum TagBuilder { -// -// public static func buildBlock(_ components: [Tag]...) -> Tag { -// GroupTag(components.flatMap { $0 }) -// } -// -// public static func buildBlock(_ components: Tag...) -> Tag { -// GroupTag(components) -// } -// -// public static func buildOptional(_ component: Tag?) -> Tag { -// component ?? GroupTag() -// } -// -// public static func buildEither(first component: Tag) -> Tag { -// component -// } -// -// public static func buildEither(second component: Tag) -> Tag { -// component -// } -// -// public static func buildArray(_ components: [Tag]) -> Tag { -// GroupTag(components) -// } -//} -// +private struct Hidden: Element { + + var node: Node? { nil } + var children: [any Element] + + init(children: [any Element] = []) { + self.children = children + } +} + +extension Builder where T == Element { + + public static func buildBlock(_ components: [Element]...) -> Element { + Hidden(children: components.flatMap { $0 }) + } + + public static func buildBlock(_ components: Element...) -> Element { + Hidden(children: components) + } + + public static func buildOptional(_ component: Element?) -> Element { + component ?? Hidden() + } + + public static func buildArray(_ components: [Element]) -> Element { + Hidden(children: components) + } + +} diff --git a/Sources/SwiftSgml/Document/Document+Renderer.swift b/Sources/SwiftSgml/Document/Document+Renderer.swift index fd9b403..6e0384d 100644 --- a/Sources/SwiftSgml/Document/Document+Renderer.swift +++ b/Sources/SwiftSgml/Document/Document+Renderer.swift @@ -5,8 +5,6 @@ // Created by Tibor Bodecs on 24/02/2024. // - - public struct DocumentRenderer { public struct Options: OptionSet, Sendable { @@ -38,7 +36,9 @@ public struct DocumentRenderer { var result: [String] = [] result += [renderDocumentType(document.type)] - result += render(document.root.node) + if let node = document.root.node { + result += render(node) + } return result.joined(separator: prettyPrint ? "\n" : "") } diff --git a/Sources/SwiftSgml/Interfaces/Attribute.swift b/Sources/SwiftSgml/Interfaces/Attribute.swift index 5029b1f..7748924 100644 --- a/Sources/SwiftSgml/Interfaces/Attribute.swift +++ b/Sources/SwiftSgml/Interfaces/Attribute.swift @@ -9,10 +9,3 @@ public protocol Attribute: Sendable { var key: String { get } var value: String? { get set } } - -//extension Attribute { -// -// public static func == (lhs: Self, rhs: Self) -> Bool { -// lhs.key == rhs.key -// } -//} diff --git a/Sources/SwiftSgml/Interfaces/Attributes.swift b/Sources/SwiftSgml/Interfaces/Attributes.swift index ba4b0e1..c4045e0 100644 --- a/Sources/SwiftSgml/Interfaces/Attributes.swift +++ b/Sources/SwiftSgml/Interfaces/Attributes.swift @@ -5,13 +5,7 @@ // Created by Tibor Bodecs on 24/02/2024. // -public protocol CustomAttributes { - associatedtype A: Attribute - - var attributes: A { get set } -} - -public protocol Attributes { +public protocol Attributes: Sendable { var attributes: [any Attribute] { get set } } diff --git a/Sources/SwiftSgml/Interfaces/Element+Standard.swift b/Sources/SwiftSgml/Interfaces/Element+Parent.swift similarity index 84% rename from Sources/SwiftSgml/Interfaces/Element+Standard.swift rename to Sources/SwiftSgml/Interfaces/Element+Parent.swift index a33c7d5..dcb68a4 100644 --- a/Sources/SwiftSgml/Interfaces/Element+Standard.swift +++ b/Sources/SwiftSgml/Interfaces/Element+Parent.swift @@ -12,15 +12,15 @@ public protocol ParentElement: Element { extension ParentElement { - public var node: Node { - .standard(.init(name: name), children.map { $0.node }) + public var node: Node? { + .standard(.init(name: name), children.compactMap { $0.node }) } } extension ParentElement where Self: Attributes { - public var node: Node { - .standard(.init(name: name, attributes: attributes), children.map { $0.node }) + public var node: Node? { + .standard(.init(name: name, attributes: attributes), children.compactMap { $0.node }) } } diff --git a/Sources/SwiftSgml/Interfaces/Element.swift b/Sources/SwiftSgml/Interfaces/Element.swift index c687f07..5a3f3f6 100644 --- a/Sources/SwiftSgml/Interfaces/Element.swift +++ b/Sources/SwiftSgml/Interfaces/Element.swift @@ -5,9 +5,10 @@ // Created by Tibor Bodecs on 24/02/2024. // -public protocol Element { +public protocol Element: Sendable { + var name: String { get } - var node: Node { get } + var node: Node? { get } } extension Element { diff --git a/Sources/SwiftSgml/Interfaces/Mutable.swift b/Sources/SwiftSgml/Interfaces/Mutable.swift index 78b3fc8..9a2c8f7 100644 --- a/Sources/SwiftSgml/Interfaces/Mutable.swift +++ b/Sources/SwiftSgml/Interfaces/Mutable.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 24/02/2024. // -public protocol Mutable { +public protocol Mutable: Sendable { func mutate(_ modify: (inout Self) -> Void) -> Self } diff --git a/Sources/SwiftSgml/Node/Node+Comment.swift b/Sources/SwiftSgml/Node/Node+Comment.swift index 81d4ce8..88ecbcc 100644 --- a/Sources/SwiftSgml/Node/Node+Comment.swift +++ b/Sources/SwiftSgml/Node/Node+Comment.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 24/02/2024. // -public struct CommentNode { +public struct CommentNode: Sendable { let value: String public init( diff --git a/Sources/SwiftSgml/Node/Node+Short.swift b/Sources/SwiftSgml/Node/Node+Short.swift index 600e3a4..588d824 100644 --- a/Sources/SwiftSgml/Node/Node+Short.swift +++ b/Sources/SwiftSgml/Node/Node+Short.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 24/02/2024. // -public struct ShortNode { +public struct ShortNode: Sendable { let name: String let attributes: [any Attribute] diff --git a/Sources/SwiftSgml/Node/Node+Standard.swift b/Sources/SwiftSgml/Node/Node+Standard.swift index 3072a27..15251f5 100644 --- a/Sources/SwiftSgml/Node/Node+Standard.swift +++ b/Sources/SwiftSgml/Node/Node+Standard.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 24/02/2024. // -public struct StandardNode { +public struct StandardNode: Sendable { let name: String let attributes: [any Attribute] diff --git a/Sources/SwiftSgml/Node/Node+Text.swift b/Sources/SwiftSgml/Node/Node+Text.swift index 4727870..952db53 100644 --- a/Sources/SwiftSgml/Node/Node+Text.swift +++ b/Sources/SwiftSgml/Node/Node+Text.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 24/02/2024. // -public struct TextNode { +public struct TextNode: Sendable { let value: String public init( diff --git a/Sources/SwiftSgml/Node/Node.swift b/Sources/SwiftSgml/Node/Node.swift index d758733..044f680 100644 --- a/Sources/SwiftSgml/Node/Node.swift +++ b/Sources/SwiftSgml/Node/Node.swift @@ -5,7 +5,7 @@ // Created by Tibor Bodecs on 24/02/2024. // -public enum Node { +public enum Node: Sendable { case standard(StandardNode, [Node]) case short(ShortNode) case text(TextNode) diff --git a/Tests/SwiftSgmlTests/ElementBuilderTests.swift b/Tests/SwiftSgmlTests/ElementBuilderTests.swift index b3e83d9..e399efc 100644 --- a/Tests/SwiftSgmlTests/ElementBuilderTests.swift +++ b/Tests/SwiftSgmlTests/ElementBuilderTests.swift @@ -14,10 +14,10 @@ final class TagBuilderTests: XCTestCase { let condition: Bool = false let document = Document { Root { -// if condition { -// Leaf("a") -// } Branch { + if condition { + Leaf("a") + } Leaf("b") } } diff --git a/Tests/SwiftSgmlTests/Utils/Root.swift b/Tests/SwiftSgmlTests/Utils/Root.swift index c333bd2..9f804a4 100644 --- a/Tests/SwiftSgmlTests/Utils/Root.swift +++ b/Tests/SwiftSgmlTests/Utils/Root.swift @@ -20,8 +20,8 @@ struct Root: Element, Mutable { var children: [any RootChildElement] var attributes: [any RootAttribute] - var node: Node { - .standard(.init(name: name, attributes: attributes), children.map { $0.node }) + var node: Node? { + .standard(.init(name: name, attributes: attributes), children.compactMap { $0.node }) } init( diff --git a/Sources/SwiftSgml/Elements/Text.swift b/Tests/SwiftSgmlTests/Utils/Text.swift similarity index 53% rename from Sources/SwiftSgml/Elements/Text.swift rename to Tests/SwiftSgmlTests/Utils/Text.swift index 8d33be7..78cb7e1 100644 --- a/Sources/SwiftSgml/Elements/Text.swift +++ b/Tests/SwiftSgmlTests/Utils/Text.swift @@ -5,13 +5,15 @@ // Created by Tibor Bodecs on 24/02/2024. // -public struct Text: Element { +import SwiftSgml + +struct Text: Element { let value: String - public init(_ value: String) { + init(_ value: String) { self.value = value } - public var node: Node { .text(.init(value: value)) } + var node: Node? { .text(.init(value: value)) } } From b80b6317d158091e557040142578e3666375a522 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 21:09:39 +0100 Subject: [PATCH 06/48] builder --- Sources/SwiftSgml/Builder.swift | 68 ++++-- .../Document/Document+Renderer.swift | 10 +- .../SwiftSgml/Interfaces/Element+Parent.swift | 4 +- Sources/SwiftSgml/Interfaces/Element.swift | 2 +- Sources/SwiftSgml/Node/Node.swift | 1 + Tests/SwiftSgmlTests/AttributeTests.swift | 2 +- Tests/SwiftSgmlTests/BuilderTests.swift | 211 ++++++++++++++++++ .../SwiftSgmlTests/ElementBuilderTests.swift | 202 ----------------- Tests/SwiftSgmlTests/ElementTests.swift | 1 + Tests/SwiftSgmlTests/Utils/Root.swift | 4 +- Tests/SwiftSgmlTests/Utils/Text.swift | 2 +- 11 files changed, 277 insertions(+), 230 deletions(-) create mode 100644 Tests/SwiftSgmlTests/BuilderTests.swift delete mode 100644 Tests/SwiftSgmlTests/ElementBuilderTests.swift diff --git a/Sources/SwiftSgml/Builder.swift b/Sources/SwiftSgml/Builder.swift index 8884a1f..c4e8e59 100644 --- a/Sources/SwiftSgml/Builder.swift +++ b/Sources/SwiftSgml/Builder.swift @@ -5,12 +5,34 @@ // Created by Tibor Bodecs on 12/11/2023. // +private struct Group: Element { + + var children: [any Element] + + var node: Node { + .group(children.compactMap { $0.node }) + } + + init(_ children: [any Element] = []) { + self.children = children + } +} + + @resultBuilder public enum Builder { public static func buildBlock(_ components: T...) -> [T] { components } + +// public static func buildBlock(_ components: T) -> [T] { +// [components] +// } + +// public static func buildBlock(_ components: [T]...) -> [T] { +// components.flatMap { $0 } +// } public static func buildEither(first component: T) -> T { component @@ -21,32 +43,46 @@ public enum Builder { } } -private struct Hidden: Element { +extension [SwiftSgml.Element]: SwiftSgml.Element { - var node: Node? { nil } - var children: [any Element] - - init(children: [any Element] = []) { - self.children = children + public var node: Node { + .group(map { $0.node }) } } extension Builder where T == Element { - public static func buildBlock(_ components: [Element]...) -> Element { - Hidden(children: components.flatMap { $0 }) - } +// public static func buildExpression(_ expression: [T]) -> T { +// Group(expression) +// } - public static func buildBlock(_ components: Element...) -> Element { - Hidden(children: components) - } +// public static func buildExpression(_ expression: Void) -> T { +// Group([]) +// } - public static func buildOptional(_ component: Element?) -> Element { - component ?? Hidden() + public static func buildBlock(_ components: T...) -> T { + Group(components) } - public static func buildArray(_ components: [Element]) -> Element { - Hidden(children: components) + public static func buildOptional(_ component: T?) -> T { + component ?? Group() + } + + public static func buildArray(_ components: [T]) -> T { + Group(components) + } + + public static func buildBlock(_ components: [T]...) -> [T] { + components.flatMap { $0 } + } + + public static func buildBlock(_ components: [T]...) -> T { + Group(components.map { Group($0) }) } + public static func buildBlock(_ components: [[T]]) -> [T] { + components.map { Group($0) } + } } + + diff --git a/Sources/SwiftSgml/Document/Document+Renderer.swift b/Sources/SwiftSgml/Document/Document+Renderer.swift index 6e0384d..a6242a0 100644 --- a/Sources/SwiftSgml/Document/Document+Renderer.swift +++ b/Sources/SwiftSgml/Document/Document+Renderer.swift @@ -36,9 +36,7 @@ public struct DocumentRenderer { var result: [String] = [] result += [renderDocumentType(document.type)] - if let node = document.root.node { - result += render(node) - } + result += render(document.root.node) return result.joined(separator: prettyPrint ? "\n" : "") } @@ -62,14 +60,16 @@ private extension DocumentRenderer { func render(_ node: Node) -> [String] { var result: [String] = [] switch node { + case .group(let children): + for child in children { + result += render(child) + } case .standard(let node, let children): result += ["<\(node.name)\(attr(node.attributes))>"] for child in children { result += render(child) } result += [""] - - case .short(let node): result += ["<\(node.name)\(attr(node.attributes))>"] case .text(let node): diff --git a/Sources/SwiftSgml/Interfaces/Element+Parent.swift b/Sources/SwiftSgml/Interfaces/Element+Parent.swift index dcb68a4..7b389d5 100644 --- a/Sources/SwiftSgml/Interfaces/Element+Parent.swift +++ b/Sources/SwiftSgml/Interfaces/Element+Parent.swift @@ -12,14 +12,14 @@ public protocol ParentElement: Element { extension ParentElement { - public var node: Node? { + public var node: Node { .standard(.init(name: name), children.compactMap { $0.node }) } } extension ParentElement where Self: Attributes { - public var node: Node? { + public var node: Node { .standard(.init(name: name, attributes: attributes), children.compactMap { $0.node }) } } diff --git a/Sources/SwiftSgml/Interfaces/Element.swift b/Sources/SwiftSgml/Interfaces/Element.swift index 5a3f3f6..d8820f3 100644 --- a/Sources/SwiftSgml/Interfaces/Element.swift +++ b/Sources/SwiftSgml/Interfaces/Element.swift @@ -8,7 +8,7 @@ public protocol Element: Sendable { var name: String { get } - var node: Node? { get } + var node: Node { get } } extension Element { diff --git a/Sources/SwiftSgml/Node/Node.swift b/Sources/SwiftSgml/Node/Node.swift index 044f680..a52485c 100644 --- a/Sources/SwiftSgml/Node/Node.swift +++ b/Sources/SwiftSgml/Node/Node.swift @@ -10,4 +10,5 @@ public enum Node: Sendable { case short(ShortNode) case text(TextNode) case comment(CommentNode) + case group([Node]) } diff --git a/Tests/SwiftSgmlTests/AttributeTests.swift b/Tests/SwiftSgmlTests/AttributeTests.swift index e772411..6cc6fe8 100644 --- a/Tests/SwiftSgmlTests/AttributeTests.swift +++ b/Tests/SwiftSgmlTests/AttributeTests.swift @@ -50,7 +50,7 @@ final class AttributeTests: XCTestCase { } func testFlagAttribute() { - let document = Document() { + let document = Document { Root { Custom(key: "foo", value: nil) } diff --git a/Tests/SwiftSgmlTests/BuilderTests.swift b/Tests/SwiftSgmlTests/BuilderTests.swift new file mode 100644 index 0000000..4a08fa1 --- /dev/null +++ b/Tests/SwiftSgmlTests/BuilderTests.swift @@ -0,0 +1,211 @@ +// +// TagBuilderTests.swift +// SwiftHtmlTests +// +// Created by Tibor Bodecs on 2021. 07. 11.. +// + +import XCTest +@testable import SwiftSgml + +final class BuilderTests: XCTestCase { + + func testOptional() { + let condition: Bool = false + let document = Document { + Root { + Branch { + if condition { + Leaf("a") + } + Leaf("b") + } + } + } + + let expectation = """ + + + b + + + """ + + XCTAssertDocument(document, expectation) + } + + func testIf() { + let condition: Bool = true + let document = Document { + Branch { + if condition { + Leaf("a") + } + else { + Leaf("b") + } + Leaf("c") + } + } + + let expectation = """ + + a + c + + """ + + XCTAssertDocument(document, expectation) + } + + func testElse() { + let condition: Bool = false + let document = Document { + Branch { + if condition { + Leaf("a") + } + else { + Leaf("b") + } + Leaf("c") + } + } + + let expectation = """ + + b + c + + """ + + XCTAssertDocument(document, expectation) + } + + + func testBlock1() { + let document = Document { + Branch { + Leaf("Lorem") + } + } + + let expectation = """ + + Lorem + + """ + XCTAssertDocument(document, expectation) + } + + func testBlock2() { + let document = Document { + Branch { + Leaf("foo") + Leaf("bar") + } + } + + let expectation = """ + + foo + bar + + """ + XCTAssertDocument(document, expectation) + } + + func testBlock3() { + let document = Document { + Branch { + Leaf("Lorem") + Branch { + Leaf("Dolor") + } + } + } + + let expectation = """ + + Lorem + + Dolor + + + """ + XCTAssertDocument(document, expectation) + } + + + func testBlock4() { + let document = Document { + Branch { + [ + Leaf("a") + ] + [ + Leaf("b") + ] + } + } + + let expectation = """ + + a + b + + """ + XCTAssertDocument(document, expectation) + } + + + func testArray1() { + let document = Document { + Branch { + for i in 0..<3 { + Leaf(String(i + 1)) + } + } + } + + let expectation = """ + + 1 + 2 + 3 + + """ + XCTAssertDocument(document, expectation) + } + + func testArray2() { + let values: [String] = ["a", "b", "c"] + + let document = Document { + Branch { + [ + Leaf("foo") + ] + values.map { item -> [Element] in + [ + Leaf(item), + Leaf(item) + ] + } + } + } + + let expectation = """ + + foo + a + a + b + b + c + c + + """ + XCTAssertDocument(document, expectation) + } +} diff --git a/Tests/SwiftSgmlTests/ElementBuilderTests.swift b/Tests/SwiftSgmlTests/ElementBuilderTests.swift deleted file mode 100644 index e399efc..0000000 --- a/Tests/SwiftSgmlTests/ElementBuilderTests.swift +++ /dev/null @@ -1,202 +0,0 @@ -// -// TagBuilderTests.swift -// SwiftHtmlTests -// -// Created by Tibor Bodecs on 2021. 07. 11.. -// - -import XCTest -@testable import SwiftSgml - -final class TagBuilderTests: XCTestCase { - - func testOptionalBuilder() { - let condition: Bool = false - let document = Document { - Root { - Branch { - if condition { - Leaf("a") - } - Leaf("b") - } - } - } - - let expectation = """ - - - b - - - """ - - XCTAssertDocument(document, expectation) - } -// -// func testEitherFirstBuilder() { -// let condition: Bool = true -// let doc = Document { -// Branch { -// if condition { -// Leaf("a") -// } -// else { -// Leaf("b") -// } -// Leaf("c") -// } -// } -// -// let html = """ -// -// a -// c -// -// """ -// -// assert(doc: doc, html: html) -// } -// -// func testEitherSecondBuilder() { -// let condition: Bool = false -// let doc = Document { -// Branch { -// if condition { -// Leaf("a") -// } -// else { -// Leaf("b") -// } -// Leaf("c") -// } -// } -// -// let html = """ -// -// b -// c -// -// """ -// -// assert(doc: doc, html: html) -// } -// -// func testArrayBuilder() { -// let doc = Document { -// Branch { -// for i in 0..<3 { -// Leaf(String(i + 1)) -// } -// } -// } -// -// let html = """ -// -// 1 -// 2 -// 3 -// -// """ -// assert(doc: doc, html: html) -// } -// -// func testGroupBuilder() { -// let doc = Document { -// Branch { -// Leaf("foo") -// Leaf("bar") -// } -// } -// -// let html = """ -// -// foo -// bar -// -// """ -// assert(doc: doc, html: html) -// } -// -// func testSingleGroupBuilder() { -// let doc = Document { -// Branch { -// [ -// Leaf("Lorem") -// ] -// } -// } -// -// let html = """ -// -// Lorem -// -// """ -// assert(doc: doc, html: html) -// } -// -// func testMultiGroupBuilder() { -// let doc = Document { -// Branch { -// [ -// Leaf("Lorem"), -// Leaf("Dolor"), -// ] -// } -// } -// -// let html = """ -// -// Lorem -// Dolor -// -// """ -// assert(doc: doc, html: html) -// } -// -// func testGroupTagBuilderAndRenderer() { -// let doc = Document { -// Branch { -// GroupTag { -// Leaf("a") -// Leaf("b") -// } -// } -// } -// -// let html = """ -// -// a -// b -// -// """ -// assert(doc: doc, html: html) -// } -// -// func testMultiGroupTagBuilderAndRenderer() { -// let values: [String] = ["a", "b", "c"] -// -// let doc = Document { -// Branch { -// values.map { item -> Tag in -// GroupTag { -// Leaf(item) -// Leaf(item) -// } -// } -// } -// } -// -// let html = """ -// -// a -// a -// b -// b -// c -// c -// -// """ -// assert(doc: doc, html: html) -// } -} diff --git a/Tests/SwiftSgmlTests/ElementTests.swift b/Tests/SwiftSgmlTests/ElementTests.swift index f5ed5d9..2b95d3e 100644 --- a/Tests/SwiftSgmlTests/ElementTests.swift +++ b/Tests/SwiftSgmlTests/ElementTests.swift @@ -8,6 +8,7 @@ import XCTest import SwiftSgml + final class TagTests: XCTestCase { func testBasicStructure() { diff --git a/Tests/SwiftSgmlTests/Utils/Root.swift b/Tests/SwiftSgmlTests/Utils/Root.swift index 9f804a4..1f3b881 100644 --- a/Tests/SwiftSgmlTests/Utils/Root.swift +++ b/Tests/SwiftSgmlTests/Utils/Root.swift @@ -20,7 +20,7 @@ struct Root: Element, Mutable { var children: [any RootChildElement] var attributes: [any RootAttribute] - var node: Node? { + var node: Node { .standard(.init(name: name, attributes: attributes), children.compactMap { $0.node }) } @@ -35,7 +35,7 @@ struct Root: Element, Mutable { init( @Builder _ b1: () -> [RootChildElement] = { [] } ) { - self.init(attributes: {}, elements: b1) + self.init(attributes: { }, elements: b1) } // MARK: - diff --git a/Tests/SwiftSgmlTests/Utils/Text.swift b/Tests/SwiftSgmlTests/Utils/Text.swift index 78cb7e1..1efebe1 100644 --- a/Tests/SwiftSgmlTests/Utils/Text.swift +++ b/Tests/SwiftSgmlTests/Utils/Text.swift @@ -15,5 +15,5 @@ struct Text: Element { self.value = value } - var node: Node? { .text(.init(value: value)) } + var node: Node { .text(.init(value: value)) } } From f7c757355694b071a38d68b50c0a4a559f89e35f Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 22:37:47 +0100 Subject: [PATCH 07/48] add back tests --- Sources/SwiftHtml/{Tags => Elements}/A.swift | 0 .../SwiftHtml/{Tags => Elements}/Abbr.swift | 0 .../{Tags => Elements}/Address.swift | 0 .../SwiftHtml/{Tags => Elements}/Area.swift | 0 .../{Tags => Elements}/Article.swift | 0 .../SwiftHtml/{Tags => Elements}/Aside.swift | 0 .../SwiftHtml/{Tags => Elements}/Audio.swift | 0 Sources/SwiftHtml/{Tags => Elements}/B.swift | 0 .../SwiftHtml/{Tags => Elements}/Base.swift | 0 .../SwiftHtml/{Tags => Elements}/Bdi.swift | 0 .../SwiftHtml/{Tags => Elements}/Bdo.swift | 0 .../{Tags => Elements}/Blockquote.swift | 0 .../SwiftHtml/{Tags => Elements}/Body.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Br.swift | 0 .../SwiftHtml/{Tags => Elements}/Button.swift | 0 .../SwiftHtml/{Tags => Elements}/Canvas.swift | 0 .../{Tags => Elements}/Caption.swift | 0 .../SwiftHtml/{Tags => Elements}/Cite.swift | 0 .../SwiftHtml/{Tags => Elements}/Code.swift | 0 .../SwiftHtml/{Tags => Elements}/Col.swift | 0 .../{Tags => Elements}/Colgroup.swift | 0 .../{Tags => Elements}/Comment.swift | 0 .../SwiftHtml/{Tags => Elements}/Data.swift | 0 .../{Tags => Elements}/Datalist.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Dd.swift | 0 .../SwiftHtml/{Tags => Elements}/Del.swift | 0 .../{Tags => Elements}/Details.swift | 0 .../SwiftHtml/{Tags => Elements}/Dfn.swift | 0 .../SwiftHtml/{Tags => Elements}/Dialog.swift | 0 .../SwiftHtml/{Tags => Elements}/Div.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Dl.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Dt.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Em.swift | 0 .../SwiftHtml/{Tags => Elements}/Embed.swift | 0 .../{Tags => Elements}/Fieldset.swift | 0 .../{Tags => Elements}/Figcaption.swift | 0 .../SwiftHtml/{Tags => Elements}/Figure.swift | 0 .../SwiftHtml/{Tags => Elements}/Footer.swift | 0 .../SwiftHtml/{Tags => Elements}/Form.swift | 0 Sources/SwiftHtml/{Tags => Elements}/H1.swift | 0 Sources/SwiftHtml/{Tags => Elements}/H2.swift | 0 Sources/SwiftHtml/{Tags => Elements}/H3.swift | 0 Sources/SwiftHtml/{Tags => Elements}/H4.swift | 0 Sources/SwiftHtml/{Tags => Elements}/H5.swift | 0 Sources/SwiftHtml/{Tags => Elements}/H6.swift | 0 .../SwiftHtml/{Tags => Elements}/Head.swift | 0 .../SwiftHtml/{Tags => Elements}/Header.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Hr.swift | 0 .../SwiftHtml/{Tags => Elements}/Html.swift | 0 Sources/SwiftHtml/{Tags => Elements}/I.swift | 0 .../SwiftHtml/{Tags => Elements}/Iframe.swift | 0 .../SwiftHtml/{Tags => Elements}/Img.swift | 0 .../SwiftHtml/{Tags => Elements}/Input.swift | 0 .../SwiftHtml/{Tags => Elements}/Ins.swift | 0 .../SwiftHtml/{Tags => Elements}/Kbd.swift | 0 .../SwiftHtml/{Tags => Elements}/Label.swift | 0 .../SwiftHtml/{Tags => Elements}/Legend.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Li.swift | 0 .../SwiftHtml/{Tags => Elements}/Link.swift | 0 .../{Tags => Elements}/MainTag.swift | 0 .../SwiftHtml/{Tags => Elements}/Map.swift | 0 .../SwiftHtml/{Tags => Elements}/Mark.swift | 0 .../SwiftHtml/{Tags => Elements}/Meta.swift | 0 .../SwiftHtml/{Tags => Elements}/Meter.swift | 0 .../SwiftHtml/{Tags => Elements}/Nav.swift | 0 .../{Tags => Elements}/Noscript.swift | 0 .../SwiftHtml/{Tags => Elements}/Object.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Ol.swift | 0 .../{Tags => Elements}/Optgroup.swift | 0 .../SwiftHtml/{Tags => Elements}/Option.swift | 0 .../SwiftHtml/{Tags => Elements}/Output.swift | 0 Sources/SwiftHtml/{Tags => Elements}/P.swift | 0 .../SwiftHtml/{Tags => Elements}/Param.swift | 0 .../{Tags => Elements}/Picture.swift | 0 .../SwiftHtml/{Tags => Elements}/Pre.swift | 0 .../{Tags => Elements}/Progress.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Q.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Rp.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Rt.swift | 0 .../SwiftHtml/{Tags => Elements}/Ruby.swift | 0 Sources/SwiftHtml/{Tags => Elements}/S.swift | 0 .../SwiftHtml/{Tags => Elements}/Samp.swift | 0 .../SwiftHtml/{Tags => Elements}/Script.swift | 0 .../{Tags => Elements}/Section.swift | 0 .../SwiftHtml/{Tags => Elements}/Select.swift | 0 .../SwiftHtml/{Tags => Elements}/Small.swift | 0 .../SwiftHtml/{Tags => Elements}/Source.swift | 0 .../SwiftHtml/{Tags => Elements}/Span.swift | 0 .../SwiftHtml/{Tags => Elements}/Strong.swift | 0 .../SwiftHtml/{Tags => Elements}/Style.swift | 0 .../SwiftHtml/{Tags => Elements}/Sub.swift | 0 .../{Tags => Elements}/Summary.swift | 0 .../SwiftHtml/{Tags => Elements}/Sup.swift | 0 .../SwiftHtml/{Tags => Elements}/Table.swift | 0 .../SwiftHtml/{Tags => Elements}/Tbody.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Td.swift | 0 .../{Tags => Elements}/Template.swift | 0 .../{Tags => Elements}/Textarea.swift | 0 .../SwiftHtml/{Tags => Elements}/Tfoot.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Th.swift | 0 .../SwiftHtml/{Tags => Elements}/Thead.swift | 0 .../SwiftHtml/{Tags => Elements}/Time.swift | 0 .../SwiftHtml/{Tags => Elements}/Title.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Tr.swift | 0 .../SwiftHtml/{Tags => Elements}/Track.swift | 0 Sources/SwiftHtml/{Tags => Elements}/U.swift | 0 Sources/SwiftHtml/{Tags => Elements}/Ul.swift | 0 .../SwiftHtml/{Tags => Elements}/Var.swift | 0 .../SwiftHtml/{Tags => Elements}/Video.swift | 0 .../SwiftHtml/{Tags => Elements}/Wbr.swift | 0 Sources/SwiftSgml/Builder.swift | 8 +- .../Document/Document+Renderer.swift | 4 +- Sources/SwiftSgml/Interfaces/Attributes.swift | 21 ++- .../SwiftSgml/Interfaces/Element+Parent.swift | 2 +- Sources/SwiftSgml/Node/Node+Short.swift | 4 +- Sources/SwiftSgml/Node/Node+Standard.swift | 4 +- Tests/SwiftSgmlTests/AttributeTests.swift | 156 +++++++++--------- Tests/SwiftSgmlTests/BuilderTests.swift | 6 +- ...iftSgmlTests.swift => DocumentTests.swift} | 4 +- Tests/SwiftSgmlTests/ElementTests.swift | 6 +- Tests/SwiftSgmlTests/Utils/Branch.swift | 2 +- Tests/SwiftSgmlTests/Utils/Leaf.swift | 6 +- Tests/SwiftSgmlTests/Utils/Root.swift | 1 + 123 files changed, 121 insertions(+), 103 deletions(-) rename Sources/SwiftHtml/{Tags => Elements}/A.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Abbr.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Address.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Area.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Article.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Aside.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Audio.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/B.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Base.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Bdi.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Bdo.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Blockquote.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Body.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Br.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Button.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Canvas.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Caption.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Cite.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Code.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Col.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Colgroup.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Comment.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Data.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Datalist.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Dd.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Del.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Details.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Dfn.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Dialog.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Div.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Dl.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Dt.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Em.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Embed.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Fieldset.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Figcaption.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Figure.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Footer.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Form.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/H1.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/H2.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/H3.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/H4.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/H5.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/H6.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Head.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Header.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Hr.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Html.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/I.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Iframe.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Img.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Input.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Ins.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Kbd.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Label.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Legend.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Li.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Link.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/MainTag.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Map.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Mark.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Meta.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Meter.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Nav.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Noscript.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Object.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Ol.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Optgroup.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Option.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Output.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/P.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Param.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Picture.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Pre.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Progress.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Q.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Rp.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Rt.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Ruby.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/S.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Samp.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Script.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Section.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Select.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Small.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Source.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Span.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Strong.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Style.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Sub.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Summary.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Sup.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Table.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Tbody.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Td.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Template.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Textarea.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Tfoot.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Th.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Thead.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Time.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Title.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Tr.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Track.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/U.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Ul.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Var.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Video.swift (100%) rename Sources/SwiftHtml/{Tags => Elements}/Wbr.swift (100%) rename Tests/SwiftSgmlTests/{SwiftSgmlTests.swift => DocumentTests.swift} (84%) diff --git a/Sources/SwiftHtml/Tags/A.swift b/Sources/SwiftHtml/Elements/A.swift similarity index 100% rename from Sources/SwiftHtml/Tags/A.swift rename to Sources/SwiftHtml/Elements/A.swift diff --git a/Sources/SwiftHtml/Tags/Abbr.swift b/Sources/SwiftHtml/Elements/Abbr.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Abbr.swift rename to Sources/SwiftHtml/Elements/Abbr.swift diff --git a/Sources/SwiftHtml/Tags/Address.swift b/Sources/SwiftHtml/Elements/Address.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Address.swift rename to Sources/SwiftHtml/Elements/Address.swift diff --git a/Sources/SwiftHtml/Tags/Area.swift b/Sources/SwiftHtml/Elements/Area.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Area.swift rename to Sources/SwiftHtml/Elements/Area.swift diff --git a/Sources/SwiftHtml/Tags/Article.swift b/Sources/SwiftHtml/Elements/Article.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Article.swift rename to Sources/SwiftHtml/Elements/Article.swift diff --git a/Sources/SwiftHtml/Tags/Aside.swift b/Sources/SwiftHtml/Elements/Aside.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Aside.swift rename to Sources/SwiftHtml/Elements/Aside.swift diff --git a/Sources/SwiftHtml/Tags/Audio.swift b/Sources/SwiftHtml/Elements/Audio.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Audio.swift rename to Sources/SwiftHtml/Elements/Audio.swift diff --git a/Sources/SwiftHtml/Tags/B.swift b/Sources/SwiftHtml/Elements/B.swift similarity index 100% rename from Sources/SwiftHtml/Tags/B.swift rename to Sources/SwiftHtml/Elements/B.swift diff --git a/Sources/SwiftHtml/Tags/Base.swift b/Sources/SwiftHtml/Elements/Base.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Base.swift rename to Sources/SwiftHtml/Elements/Base.swift diff --git a/Sources/SwiftHtml/Tags/Bdi.swift b/Sources/SwiftHtml/Elements/Bdi.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Bdi.swift rename to Sources/SwiftHtml/Elements/Bdi.swift diff --git a/Sources/SwiftHtml/Tags/Bdo.swift b/Sources/SwiftHtml/Elements/Bdo.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Bdo.swift rename to Sources/SwiftHtml/Elements/Bdo.swift diff --git a/Sources/SwiftHtml/Tags/Blockquote.swift b/Sources/SwiftHtml/Elements/Blockquote.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Blockquote.swift rename to Sources/SwiftHtml/Elements/Blockquote.swift diff --git a/Sources/SwiftHtml/Tags/Body.swift b/Sources/SwiftHtml/Elements/Body.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Body.swift rename to Sources/SwiftHtml/Elements/Body.swift diff --git a/Sources/SwiftHtml/Tags/Br.swift b/Sources/SwiftHtml/Elements/Br.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Br.swift rename to Sources/SwiftHtml/Elements/Br.swift diff --git a/Sources/SwiftHtml/Tags/Button.swift b/Sources/SwiftHtml/Elements/Button.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Button.swift rename to Sources/SwiftHtml/Elements/Button.swift diff --git a/Sources/SwiftHtml/Tags/Canvas.swift b/Sources/SwiftHtml/Elements/Canvas.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Canvas.swift rename to Sources/SwiftHtml/Elements/Canvas.swift diff --git a/Sources/SwiftHtml/Tags/Caption.swift b/Sources/SwiftHtml/Elements/Caption.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Caption.swift rename to Sources/SwiftHtml/Elements/Caption.swift diff --git a/Sources/SwiftHtml/Tags/Cite.swift b/Sources/SwiftHtml/Elements/Cite.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Cite.swift rename to Sources/SwiftHtml/Elements/Cite.swift diff --git a/Sources/SwiftHtml/Tags/Code.swift b/Sources/SwiftHtml/Elements/Code.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Code.swift rename to Sources/SwiftHtml/Elements/Code.swift diff --git a/Sources/SwiftHtml/Tags/Col.swift b/Sources/SwiftHtml/Elements/Col.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Col.swift rename to Sources/SwiftHtml/Elements/Col.swift diff --git a/Sources/SwiftHtml/Tags/Colgroup.swift b/Sources/SwiftHtml/Elements/Colgroup.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Colgroup.swift rename to Sources/SwiftHtml/Elements/Colgroup.swift diff --git a/Sources/SwiftHtml/Tags/Comment.swift b/Sources/SwiftHtml/Elements/Comment.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Comment.swift rename to Sources/SwiftHtml/Elements/Comment.swift diff --git a/Sources/SwiftHtml/Tags/Data.swift b/Sources/SwiftHtml/Elements/Data.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Data.swift rename to Sources/SwiftHtml/Elements/Data.swift diff --git a/Sources/SwiftHtml/Tags/Datalist.swift b/Sources/SwiftHtml/Elements/Datalist.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Datalist.swift rename to Sources/SwiftHtml/Elements/Datalist.swift diff --git a/Sources/SwiftHtml/Tags/Dd.swift b/Sources/SwiftHtml/Elements/Dd.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Dd.swift rename to Sources/SwiftHtml/Elements/Dd.swift diff --git a/Sources/SwiftHtml/Tags/Del.swift b/Sources/SwiftHtml/Elements/Del.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Del.swift rename to Sources/SwiftHtml/Elements/Del.swift diff --git a/Sources/SwiftHtml/Tags/Details.swift b/Sources/SwiftHtml/Elements/Details.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Details.swift rename to Sources/SwiftHtml/Elements/Details.swift diff --git a/Sources/SwiftHtml/Tags/Dfn.swift b/Sources/SwiftHtml/Elements/Dfn.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Dfn.swift rename to Sources/SwiftHtml/Elements/Dfn.swift diff --git a/Sources/SwiftHtml/Tags/Dialog.swift b/Sources/SwiftHtml/Elements/Dialog.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Dialog.swift rename to Sources/SwiftHtml/Elements/Dialog.swift diff --git a/Sources/SwiftHtml/Tags/Div.swift b/Sources/SwiftHtml/Elements/Div.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Div.swift rename to Sources/SwiftHtml/Elements/Div.swift diff --git a/Sources/SwiftHtml/Tags/Dl.swift b/Sources/SwiftHtml/Elements/Dl.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Dl.swift rename to Sources/SwiftHtml/Elements/Dl.swift diff --git a/Sources/SwiftHtml/Tags/Dt.swift b/Sources/SwiftHtml/Elements/Dt.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Dt.swift rename to Sources/SwiftHtml/Elements/Dt.swift diff --git a/Sources/SwiftHtml/Tags/Em.swift b/Sources/SwiftHtml/Elements/Em.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Em.swift rename to Sources/SwiftHtml/Elements/Em.swift diff --git a/Sources/SwiftHtml/Tags/Embed.swift b/Sources/SwiftHtml/Elements/Embed.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Embed.swift rename to Sources/SwiftHtml/Elements/Embed.swift diff --git a/Sources/SwiftHtml/Tags/Fieldset.swift b/Sources/SwiftHtml/Elements/Fieldset.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Fieldset.swift rename to Sources/SwiftHtml/Elements/Fieldset.swift diff --git a/Sources/SwiftHtml/Tags/Figcaption.swift b/Sources/SwiftHtml/Elements/Figcaption.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Figcaption.swift rename to Sources/SwiftHtml/Elements/Figcaption.swift diff --git a/Sources/SwiftHtml/Tags/Figure.swift b/Sources/SwiftHtml/Elements/Figure.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Figure.swift rename to Sources/SwiftHtml/Elements/Figure.swift diff --git a/Sources/SwiftHtml/Tags/Footer.swift b/Sources/SwiftHtml/Elements/Footer.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Footer.swift rename to Sources/SwiftHtml/Elements/Footer.swift diff --git a/Sources/SwiftHtml/Tags/Form.swift b/Sources/SwiftHtml/Elements/Form.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Form.swift rename to Sources/SwiftHtml/Elements/Form.swift diff --git a/Sources/SwiftHtml/Tags/H1.swift b/Sources/SwiftHtml/Elements/H1.swift similarity index 100% rename from Sources/SwiftHtml/Tags/H1.swift rename to Sources/SwiftHtml/Elements/H1.swift diff --git a/Sources/SwiftHtml/Tags/H2.swift b/Sources/SwiftHtml/Elements/H2.swift similarity index 100% rename from Sources/SwiftHtml/Tags/H2.swift rename to Sources/SwiftHtml/Elements/H2.swift diff --git a/Sources/SwiftHtml/Tags/H3.swift b/Sources/SwiftHtml/Elements/H3.swift similarity index 100% rename from Sources/SwiftHtml/Tags/H3.swift rename to Sources/SwiftHtml/Elements/H3.swift diff --git a/Sources/SwiftHtml/Tags/H4.swift b/Sources/SwiftHtml/Elements/H4.swift similarity index 100% rename from Sources/SwiftHtml/Tags/H4.swift rename to Sources/SwiftHtml/Elements/H4.swift diff --git a/Sources/SwiftHtml/Tags/H5.swift b/Sources/SwiftHtml/Elements/H5.swift similarity index 100% rename from Sources/SwiftHtml/Tags/H5.swift rename to Sources/SwiftHtml/Elements/H5.swift diff --git a/Sources/SwiftHtml/Tags/H6.swift b/Sources/SwiftHtml/Elements/H6.swift similarity index 100% rename from Sources/SwiftHtml/Tags/H6.swift rename to Sources/SwiftHtml/Elements/H6.swift diff --git a/Sources/SwiftHtml/Tags/Head.swift b/Sources/SwiftHtml/Elements/Head.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Head.swift rename to Sources/SwiftHtml/Elements/Head.swift diff --git a/Sources/SwiftHtml/Tags/Header.swift b/Sources/SwiftHtml/Elements/Header.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Header.swift rename to Sources/SwiftHtml/Elements/Header.swift diff --git a/Sources/SwiftHtml/Tags/Hr.swift b/Sources/SwiftHtml/Elements/Hr.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Hr.swift rename to Sources/SwiftHtml/Elements/Hr.swift diff --git a/Sources/SwiftHtml/Tags/Html.swift b/Sources/SwiftHtml/Elements/Html.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Html.swift rename to Sources/SwiftHtml/Elements/Html.swift diff --git a/Sources/SwiftHtml/Tags/I.swift b/Sources/SwiftHtml/Elements/I.swift similarity index 100% rename from Sources/SwiftHtml/Tags/I.swift rename to Sources/SwiftHtml/Elements/I.swift diff --git a/Sources/SwiftHtml/Tags/Iframe.swift b/Sources/SwiftHtml/Elements/Iframe.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Iframe.swift rename to Sources/SwiftHtml/Elements/Iframe.swift diff --git a/Sources/SwiftHtml/Tags/Img.swift b/Sources/SwiftHtml/Elements/Img.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Img.swift rename to Sources/SwiftHtml/Elements/Img.swift diff --git a/Sources/SwiftHtml/Tags/Input.swift b/Sources/SwiftHtml/Elements/Input.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Input.swift rename to Sources/SwiftHtml/Elements/Input.swift diff --git a/Sources/SwiftHtml/Tags/Ins.swift b/Sources/SwiftHtml/Elements/Ins.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Ins.swift rename to Sources/SwiftHtml/Elements/Ins.swift diff --git a/Sources/SwiftHtml/Tags/Kbd.swift b/Sources/SwiftHtml/Elements/Kbd.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Kbd.swift rename to Sources/SwiftHtml/Elements/Kbd.swift diff --git a/Sources/SwiftHtml/Tags/Label.swift b/Sources/SwiftHtml/Elements/Label.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Label.swift rename to Sources/SwiftHtml/Elements/Label.swift diff --git a/Sources/SwiftHtml/Tags/Legend.swift b/Sources/SwiftHtml/Elements/Legend.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Legend.swift rename to Sources/SwiftHtml/Elements/Legend.swift diff --git a/Sources/SwiftHtml/Tags/Li.swift b/Sources/SwiftHtml/Elements/Li.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Li.swift rename to Sources/SwiftHtml/Elements/Li.swift diff --git a/Sources/SwiftHtml/Tags/Link.swift b/Sources/SwiftHtml/Elements/Link.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Link.swift rename to Sources/SwiftHtml/Elements/Link.swift diff --git a/Sources/SwiftHtml/Tags/MainTag.swift b/Sources/SwiftHtml/Elements/MainTag.swift similarity index 100% rename from Sources/SwiftHtml/Tags/MainTag.swift rename to Sources/SwiftHtml/Elements/MainTag.swift diff --git a/Sources/SwiftHtml/Tags/Map.swift b/Sources/SwiftHtml/Elements/Map.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Map.swift rename to Sources/SwiftHtml/Elements/Map.swift diff --git a/Sources/SwiftHtml/Tags/Mark.swift b/Sources/SwiftHtml/Elements/Mark.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Mark.swift rename to Sources/SwiftHtml/Elements/Mark.swift diff --git a/Sources/SwiftHtml/Tags/Meta.swift b/Sources/SwiftHtml/Elements/Meta.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Meta.swift rename to Sources/SwiftHtml/Elements/Meta.swift diff --git a/Sources/SwiftHtml/Tags/Meter.swift b/Sources/SwiftHtml/Elements/Meter.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Meter.swift rename to Sources/SwiftHtml/Elements/Meter.swift diff --git a/Sources/SwiftHtml/Tags/Nav.swift b/Sources/SwiftHtml/Elements/Nav.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Nav.swift rename to Sources/SwiftHtml/Elements/Nav.swift diff --git a/Sources/SwiftHtml/Tags/Noscript.swift b/Sources/SwiftHtml/Elements/Noscript.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Noscript.swift rename to Sources/SwiftHtml/Elements/Noscript.swift diff --git a/Sources/SwiftHtml/Tags/Object.swift b/Sources/SwiftHtml/Elements/Object.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Object.swift rename to Sources/SwiftHtml/Elements/Object.swift diff --git a/Sources/SwiftHtml/Tags/Ol.swift b/Sources/SwiftHtml/Elements/Ol.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Ol.swift rename to Sources/SwiftHtml/Elements/Ol.swift diff --git a/Sources/SwiftHtml/Tags/Optgroup.swift b/Sources/SwiftHtml/Elements/Optgroup.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Optgroup.swift rename to Sources/SwiftHtml/Elements/Optgroup.swift diff --git a/Sources/SwiftHtml/Tags/Option.swift b/Sources/SwiftHtml/Elements/Option.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Option.swift rename to Sources/SwiftHtml/Elements/Option.swift diff --git a/Sources/SwiftHtml/Tags/Output.swift b/Sources/SwiftHtml/Elements/Output.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Output.swift rename to Sources/SwiftHtml/Elements/Output.swift diff --git a/Sources/SwiftHtml/Tags/P.swift b/Sources/SwiftHtml/Elements/P.swift similarity index 100% rename from Sources/SwiftHtml/Tags/P.swift rename to Sources/SwiftHtml/Elements/P.swift diff --git a/Sources/SwiftHtml/Tags/Param.swift b/Sources/SwiftHtml/Elements/Param.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Param.swift rename to Sources/SwiftHtml/Elements/Param.swift diff --git a/Sources/SwiftHtml/Tags/Picture.swift b/Sources/SwiftHtml/Elements/Picture.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Picture.swift rename to Sources/SwiftHtml/Elements/Picture.swift diff --git a/Sources/SwiftHtml/Tags/Pre.swift b/Sources/SwiftHtml/Elements/Pre.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Pre.swift rename to Sources/SwiftHtml/Elements/Pre.swift diff --git a/Sources/SwiftHtml/Tags/Progress.swift b/Sources/SwiftHtml/Elements/Progress.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Progress.swift rename to Sources/SwiftHtml/Elements/Progress.swift diff --git a/Sources/SwiftHtml/Tags/Q.swift b/Sources/SwiftHtml/Elements/Q.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Q.swift rename to Sources/SwiftHtml/Elements/Q.swift diff --git a/Sources/SwiftHtml/Tags/Rp.swift b/Sources/SwiftHtml/Elements/Rp.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Rp.swift rename to Sources/SwiftHtml/Elements/Rp.swift diff --git a/Sources/SwiftHtml/Tags/Rt.swift b/Sources/SwiftHtml/Elements/Rt.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Rt.swift rename to Sources/SwiftHtml/Elements/Rt.swift diff --git a/Sources/SwiftHtml/Tags/Ruby.swift b/Sources/SwiftHtml/Elements/Ruby.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Ruby.swift rename to Sources/SwiftHtml/Elements/Ruby.swift diff --git a/Sources/SwiftHtml/Tags/S.swift b/Sources/SwiftHtml/Elements/S.swift similarity index 100% rename from Sources/SwiftHtml/Tags/S.swift rename to Sources/SwiftHtml/Elements/S.swift diff --git a/Sources/SwiftHtml/Tags/Samp.swift b/Sources/SwiftHtml/Elements/Samp.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Samp.swift rename to Sources/SwiftHtml/Elements/Samp.swift diff --git a/Sources/SwiftHtml/Tags/Script.swift b/Sources/SwiftHtml/Elements/Script.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Script.swift rename to Sources/SwiftHtml/Elements/Script.swift diff --git a/Sources/SwiftHtml/Tags/Section.swift b/Sources/SwiftHtml/Elements/Section.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Section.swift rename to Sources/SwiftHtml/Elements/Section.swift diff --git a/Sources/SwiftHtml/Tags/Select.swift b/Sources/SwiftHtml/Elements/Select.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Select.swift rename to Sources/SwiftHtml/Elements/Select.swift diff --git a/Sources/SwiftHtml/Tags/Small.swift b/Sources/SwiftHtml/Elements/Small.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Small.swift rename to Sources/SwiftHtml/Elements/Small.swift diff --git a/Sources/SwiftHtml/Tags/Source.swift b/Sources/SwiftHtml/Elements/Source.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Source.swift rename to Sources/SwiftHtml/Elements/Source.swift diff --git a/Sources/SwiftHtml/Tags/Span.swift b/Sources/SwiftHtml/Elements/Span.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Span.swift rename to Sources/SwiftHtml/Elements/Span.swift diff --git a/Sources/SwiftHtml/Tags/Strong.swift b/Sources/SwiftHtml/Elements/Strong.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Strong.swift rename to Sources/SwiftHtml/Elements/Strong.swift diff --git a/Sources/SwiftHtml/Tags/Style.swift b/Sources/SwiftHtml/Elements/Style.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Style.swift rename to Sources/SwiftHtml/Elements/Style.swift diff --git a/Sources/SwiftHtml/Tags/Sub.swift b/Sources/SwiftHtml/Elements/Sub.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Sub.swift rename to Sources/SwiftHtml/Elements/Sub.swift diff --git a/Sources/SwiftHtml/Tags/Summary.swift b/Sources/SwiftHtml/Elements/Summary.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Summary.swift rename to Sources/SwiftHtml/Elements/Summary.swift diff --git a/Sources/SwiftHtml/Tags/Sup.swift b/Sources/SwiftHtml/Elements/Sup.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Sup.swift rename to Sources/SwiftHtml/Elements/Sup.swift diff --git a/Sources/SwiftHtml/Tags/Table.swift b/Sources/SwiftHtml/Elements/Table.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Table.swift rename to Sources/SwiftHtml/Elements/Table.swift diff --git a/Sources/SwiftHtml/Tags/Tbody.swift b/Sources/SwiftHtml/Elements/Tbody.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Tbody.swift rename to Sources/SwiftHtml/Elements/Tbody.swift diff --git a/Sources/SwiftHtml/Tags/Td.swift b/Sources/SwiftHtml/Elements/Td.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Td.swift rename to Sources/SwiftHtml/Elements/Td.swift diff --git a/Sources/SwiftHtml/Tags/Template.swift b/Sources/SwiftHtml/Elements/Template.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Template.swift rename to Sources/SwiftHtml/Elements/Template.swift diff --git a/Sources/SwiftHtml/Tags/Textarea.swift b/Sources/SwiftHtml/Elements/Textarea.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Textarea.swift rename to Sources/SwiftHtml/Elements/Textarea.swift diff --git a/Sources/SwiftHtml/Tags/Tfoot.swift b/Sources/SwiftHtml/Elements/Tfoot.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Tfoot.swift rename to Sources/SwiftHtml/Elements/Tfoot.swift diff --git a/Sources/SwiftHtml/Tags/Th.swift b/Sources/SwiftHtml/Elements/Th.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Th.swift rename to Sources/SwiftHtml/Elements/Th.swift diff --git a/Sources/SwiftHtml/Tags/Thead.swift b/Sources/SwiftHtml/Elements/Thead.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Thead.swift rename to Sources/SwiftHtml/Elements/Thead.swift diff --git a/Sources/SwiftHtml/Tags/Time.swift b/Sources/SwiftHtml/Elements/Time.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Time.swift rename to Sources/SwiftHtml/Elements/Time.swift diff --git a/Sources/SwiftHtml/Tags/Title.swift b/Sources/SwiftHtml/Elements/Title.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Title.swift rename to Sources/SwiftHtml/Elements/Title.swift diff --git a/Sources/SwiftHtml/Tags/Tr.swift b/Sources/SwiftHtml/Elements/Tr.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Tr.swift rename to Sources/SwiftHtml/Elements/Tr.swift diff --git a/Sources/SwiftHtml/Tags/Track.swift b/Sources/SwiftHtml/Elements/Track.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Track.swift rename to Sources/SwiftHtml/Elements/Track.swift diff --git a/Sources/SwiftHtml/Tags/U.swift b/Sources/SwiftHtml/Elements/U.swift similarity index 100% rename from Sources/SwiftHtml/Tags/U.swift rename to Sources/SwiftHtml/Elements/U.swift diff --git a/Sources/SwiftHtml/Tags/Ul.swift b/Sources/SwiftHtml/Elements/Ul.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Ul.swift rename to Sources/SwiftHtml/Elements/Ul.swift diff --git a/Sources/SwiftHtml/Tags/Var.swift b/Sources/SwiftHtml/Elements/Var.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Var.swift rename to Sources/SwiftHtml/Elements/Var.swift diff --git a/Sources/SwiftHtml/Tags/Video.swift b/Sources/SwiftHtml/Elements/Video.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Video.swift rename to Sources/SwiftHtml/Elements/Video.swift diff --git a/Sources/SwiftHtml/Tags/Wbr.swift b/Sources/SwiftHtml/Elements/Wbr.swift similarity index 100% rename from Sources/SwiftHtml/Tags/Wbr.swift rename to Sources/SwiftHtml/Elements/Wbr.swift diff --git a/Sources/SwiftSgml/Builder.swift b/Sources/SwiftSgml/Builder.swift index c4e8e59..75259b6 100644 --- a/Sources/SwiftSgml/Builder.swift +++ b/Sources/SwiftSgml/Builder.swift @@ -7,13 +7,13 @@ private struct Group: Element { - var children: [any Element] + var children: [Element] var node: Node { .group(children.compactMap { $0.node }) } - init(_ children: [any Element] = []) { + init(_ children: [Element] = []) { self.children = children } } @@ -52,6 +52,10 @@ extension [SwiftSgml.Element]: SwiftSgml.Element { extension Builder where T == Element { +// public static func buildBlock(_ components: T) -> [T] { +// [components] +// } + // public static func buildExpression(_ expression: [T]) -> T { // Group(expression) // } diff --git a/Sources/SwiftSgml/Document/Document+Renderer.swift b/Sources/SwiftSgml/Document/Document+Renderer.swift index a6242a0..248db73 100644 --- a/Sources/SwiftSgml/Document/Document+Renderer.swift +++ b/Sources/SwiftSgml/Document/Document+Renderer.swift @@ -80,7 +80,7 @@ private extension DocumentRenderer { return result } - func attr(_ attributes: [any Attribute]) -> String { + func attr(_ attributes: [Attribute]) -> String { let attr = render(attributes).joined(separator: " ") if !attr.isEmpty { return " " + attr @@ -88,7 +88,7 @@ private extension DocumentRenderer { return attr } - func render(_ attributes: [any Attribute]) -> [String] { + func render(_ attributes: [Attribute]) -> [String] { attributes.map { attribute in if let value = attribute.value { return #"\#(attribute.key)="\#(value)""# diff --git a/Sources/SwiftSgml/Interfaces/Attributes.swift b/Sources/SwiftSgml/Interfaces/Attributes.swift index c4045e0..f9443a6 100644 --- a/Sources/SwiftSgml/Interfaces/Attributes.swift +++ b/Sources/SwiftSgml/Interfaces/Attributes.swift @@ -7,23 +7,24 @@ public protocol Attributes: Sendable { - var attributes: [any Attribute] { get set } + var attributes: [Attribute] { get set } } public protocol MutableAttributes: Attributes, Mutable { - func index(of attribute: any Attribute) -> [any Attribute].Index? - func add(attribute: any Attribute) -> Self + func index(of attribute: Attribute) -> [Attribute].Index? + func add(attribute: Attribute) -> Self func remove(attributeByKey key: String) -> Self + func set(attributes newValue: [Attribute]) -> Self } public extension MutableAttributes { - func index(of attribute: any Attribute) -> [any Attribute].Index? { + func index(of attribute: Attribute) -> [Attribute].Index? { attributes.firstIndex(where: { $0.key == attribute.key }) } - mutating func add(attribute: any Attribute) { + mutating func add(attribute: Attribute) { if let index = index(of: attribute) { attributes[index].value = attribute.value } @@ -35,6 +36,10 @@ public extension MutableAttributes { mutating func remove(attributeByKey key: String) { attributes = attributes.filter { $0.key != key} } + + mutating func set(attributes newValue: [Attribute]) { + attributes = newValue + } // MARK: - @@ -42,9 +47,15 @@ public extension MutableAttributes { mutate { $0.add(attribute: attribute) } } + func set(attributes newValue: [Attribute]) -> Self { + mutate { $0.set(attributes: newValue) } + } + func remove(attributeByKey key: String) -> Self { mutate { $0.remove(attributeByKey: key) } } + + } diff --git a/Sources/SwiftSgml/Interfaces/Element+Parent.swift b/Sources/SwiftSgml/Interfaces/Element+Parent.swift index 7b389d5..0d433bd 100644 --- a/Sources/SwiftSgml/Interfaces/Element+Parent.swift +++ b/Sources/SwiftSgml/Interfaces/Element+Parent.swift @@ -7,7 +7,7 @@ public protocol ParentElement: Element { - var children: [any Element] { get set } + var children: [Element] { get set } } extension ParentElement { diff --git a/Sources/SwiftSgml/Node/Node+Short.swift b/Sources/SwiftSgml/Node/Node+Short.swift index 588d824..9b9f521 100644 --- a/Sources/SwiftSgml/Node/Node+Short.swift +++ b/Sources/SwiftSgml/Node/Node+Short.swift @@ -7,11 +7,11 @@ public struct ShortNode: Sendable { let name: String - let attributes: [any Attribute] + let attributes: [Attribute] public init( name: String, - attributes: [any Attribute] = [] + attributes: [Attribute] = [] ) { self.name = name self.attributes = attributes diff --git a/Sources/SwiftSgml/Node/Node+Standard.swift b/Sources/SwiftSgml/Node/Node+Standard.swift index 15251f5..378a185 100644 --- a/Sources/SwiftSgml/Node/Node+Standard.swift +++ b/Sources/SwiftSgml/Node/Node+Standard.swift @@ -7,11 +7,11 @@ public struct StandardNode: Sendable { let name: String - let attributes: [any Attribute] + let attributes: [Attribute] public init( name: String, - attributes: [any Attribute] = [] + attributes: [Attribute] = [] ) { self.name = name self.attributes = attributes diff --git a/Tests/SwiftSgmlTests/AttributeTests.swift b/Tests/SwiftSgmlTests/AttributeTests.swift index 6cc6fe8..68f72cb 100644 --- a/Tests/SwiftSgmlTests/AttributeTests.swift +++ b/Tests/SwiftSgmlTests/AttributeTests.swift @@ -10,7 +10,7 @@ import XCTest final class AttributeTests: XCTestCase { - func testCustomAttribute() { + func testCustom() { let document = Document(.xml) { Root() .add(attribute: Custom(key: "foo", value: "bar")) @@ -23,7 +23,7 @@ final class AttributeTests: XCTestCase { XCTAssertDocument(document, expectation) } - func testCustomNilAttribute() { + func testCustomNilValue() { let document = Document(.xml) { Root() .add(attribute: Custom(key: "foo")) @@ -36,7 +36,7 @@ final class AttributeTests: XCTestCase { XCTAssertDocument(document, expectation) } - func testCustomEmptyAttribute() { + func testCustomEmptyValue() { let document = Document(.xml) { Root() .add(attribute: Custom(key: "foo", value: "")) @@ -49,10 +49,10 @@ final class AttributeTests: XCTestCase { XCTAssertDocument(document, expectation) } - func testFlagAttribute() { + func testFlag() { let document = Document { Root { - Custom(key: "foo", value: nil) + Flag("foo") } } @@ -64,75 +64,79 @@ final class AttributeTests: XCTestCase { // MARK: - -// func testSetAttributes() { -// -// let doc = Document { -// Leaf("example") -// .attribute("foo", "example") -// .setAttributes([ -// .init(key: "a", value: "foo"), -// .init(key: "b", value: "bar"), -// .init(key: "c", value: "baz"), -// ]) -// } -// -// let html = """ -// example -// """ -// } -// -// func testAddAttribute() { -// let doc = Document { -// Leaf("example") -// .setAttributes([ -// .init(key: "a", value: "foo"), -// .init(key: "b", value: "bar"), -// .init(key: "c", value: "baz"), -// ]) -// .attribute("foo", "example") -// } -// -// let html = """ -// example -// """ -// } -// -// func testDeleteAttribute() { -// let doc = Document { -// Leaf("example") -// .setAttributes([ -// .init(key: "a", value: "foo"), -// .init(key: "b", value: "bar"), -// .init(key: "c", value: "baz"), -// ]) -// .deleteAttribute("b") -// } -// -// let html = """ -// example -// """ -// } -// -// func testFlagAttribute() { -// let doc = Document { -// Leaf("example") -// .flagAttribute("foo") -// } -// -// let html = """ -// example -// """ -// } -// -// func testDeleteFlagAttribute() { -// let doc = Document { -// Leaf("example") -// .flagAttribute("foo") -// .deleteAttribute("foo") -// } -// -// let html = """ -// example -// """ -// } + func testSet() { + let document = Document { + Leaf("example") + .add(attribute: Custom(key: "foo", value: "example")) + .set(attributes: [ + Custom(key: "a", value: "foo"), + Custom(key: "b", value: "bar"), + Custom(key: "c", value: "baz"), + ]) + } + + let expectation = """ + example + """ + XCTAssertDocument(document, expectation) + } + + func testAdd() { + let document = Document { + Leaf("example") + .set(attributes: [ + Custom(key: "a", value: "foo"), + Custom(key: "b", value: "bar"), + Custom(key: "c", value: "baz"), + ]) + .add(attribute: Custom(key: "foo", value: "example")) + } + + let expectation = """ + example + """ + XCTAssertDocument(document, expectation) + } + + func testRemove() { + let document = Document { + Leaf("example") + .set(attributes: [ + Custom(key: "a", value: "foo"), + Custom(key: "b", value: "bar"), + Custom(key: "c", value: "baz"), + ]) + .remove(attributeByKey: "b") + } + + let expectation = """ + example + """ + XCTAssertDocument(document, expectation) + } + + func testAddFlag() { + let document = Document { + Leaf("example") + .add(attribute: Flag("foo")) + } + + let expectation = """ + example + """ + XCTAssertDocument(document, expectation) + } + + func testRemoveFlag() { + let document = Document { + Leaf("example") + .add(attribute: Flag("foo")) + .remove(attributeByKey: "foo") + } + + let expectation = """ + example + """ + XCTAssertDocument(document, expectation) + } } diff --git a/Tests/SwiftSgmlTests/BuilderTests.swift b/Tests/SwiftSgmlTests/BuilderTests.swift index 4a08fa1..4541258 100644 --- a/Tests/SwiftSgmlTests/BuilderTests.swift +++ b/Tests/SwiftSgmlTests/BuilderTests.swift @@ -140,7 +140,7 @@ final class BuilderTests: XCTestCase { func testBlock4() { let document = Document { Branch { - [ + [ // NOTE: make this work without array... Leaf("a") ] [ @@ -183,9 +183,6 @@ final class BuilderTests: XCTestCase { let document = Document { Branch { - [ - Leaf("foo") - ] values.map { item -> [Element] in [ Leaf(item), @@ -197,7 +194,6 @@ final class BuilderTests: XCTestCase { let expectation = """ - foo a a b diff --git a/Tests/SwiftSgmlTests/SwiftSgmlTests.swift b/Tests/SwiftSgmlTests/DocumentTests.swift similarity index 84% rename from Tests/SwiftSgmlTests/SwiftSgmlTests.swift rename to Tests/SwiftSgmlTests/DocumentTests.swift index 5591809..87a39fd 100644 --- a/Tests/SwiftSgmlTests/SwiftSgmlTests.swift +++ b/Tests/SwiftSgmlTests/DocumentTests.swift @@ -8,9 +8,9 @@ import XCTest @testable import SwiftSgml -final class SwiftSgmlTests: XCTestCase { +final class DocumentTests: XCTestCase { - func testXmlDocument() { + func testXml() { let document = Document(.xml) { Root() } diff --git a/Tests/SwiftSgmlTests/ElementTests.swift b/Tests/SwiftSgmlTests/ElementTests.swift index 2b95d3e..db11fb2 100644 --- a/Tests/SwiftSgmlTests/ElementTests.swift +++ b/Tests/SwiftSgmlTests/ElementTests.swift @@ -11,7 +11,7 @@ import SwiftSgml final class TagTests: XCTestCase { - func testBasicStructure() { + func testBasics() { let doc = Document { Root { Branch { @@ -34,7 +34,7 @@ final class TagTests: XCTestCase { XCTAssertEqual(expectation.minify(), result) } - func testAddChildrenStructure() { + func testAddChild() { let doc = Document { Root { Branch() @@ -58,7 +58,7 @@ final class TagTests: XCTestCase { XCTAssertEqual(expectation.minify(), result) } - func testRemoveAllChildrenStructure() { + func testRemoveAllChildren() { let doc = Document { Root { Branch { diff --git a/Tests/SwiftSgmlTests/Utils/Branch.swift b/Tests/SwiftSgmlTests/Utils/Branch.swift index ac42f9a..5f3e5ab 100644 --- a/Tests/SwiftSgmlTests/Utils/Branch.swift +++ b/Tests/SwiftSgmlTests/Utils/Branch.swift @@ -9,7 +9,7 @@ import SwiftSgml struct Branch: MutableParentElement { - var children: [any Element] + var children: [Element] public init( @Builder elements b1: () -> [Element] = { [] } diff --git a/Tests/SwiftSgmlTests/Utils/Leaf.swift b/Tests/SwiftSgmlTests/Utils/Leaf.swift index 359eba1..405fb6f 100644 --- a/Tests/SwiftSgmlTests/Utils/Leaf.swift +++ b/Tests/SwiftSgmlTests/Utils/Leaf.swift @@ -7,14 +7,16 @@ import SwiftSgml -struct Leaf: ParentElement { +struct Leaf: ParentElement, MutableAttributes { - var children: [any Element] + var children: [Element] + var attributes: [Attribute] init(_ value: String) { self.children = [ Text(value) ] + self.attributes = [] } diff --git a/Tests/SwiftSgmlTests/Utils/Root.swift b/Tests/SwiftSgmlTests/Utils/Root.swift index 1f3b881..e4ed631 100644 --- a/Tests/SwiftSgmlTests/Utils/Root.swift +++ b/Tests/SwiftSgmlTests/Utils/Root.swift @@ -14,6 +14,7 @@ extension Branch: RootChildElement {} protocol RootAttribute: Attribute {} extension Custom: RootAttribute {} +extension Flag: RootAttribute {} struct Root: Element, Mutable { From 06570a4f031f6ca83e2d5f151607b84c14560bb6 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 22:54:04 +0100 Subject: [PATCH 08/48] restructure --- Makefile | 2 +- Sources/SwiftSgml/Builder.swift | 27 ------------------- Sources/SwiftSgml/Interfaces/Element.swift | 8 +++--- .../SwiftHtmlTests/Utils/String+Minify.swift | 15 +++++++++++ .../Utils/Text.swift | 0 .../Utils/XCTAssertDocument.swift | 20 ++++++++++++++ Tests/SwiftHtmlTests/assert.swift | 17 ------------ Tests/SwiftRssTests/Utils/String+Minify.swift | 15 +++++++++++ Tests/SwiftRssTests/Utils/Text.swift | 19 +++++++++++++ .../Utils/XCTAssertDocument.swift | 20 ++++++++++++++ Tests/SwiftRssTests/assert.swift | 17 ------------ .../Utils}/Attributes/Custom.swift | 2 ++ .../Utils}/Attributes/Flag.swift | 2 ++ .../Utils/{ => Elements}/Branch.swift | 0 .../Utils/{ => Elements}/Leaf.swift | 0 .../Utils/{ => Elements}/Root.swift | 0 .../SwiftSgmlTests/Utils/Elements/Text.swift | 19 +++++++++++++ .../Utils/String+Minify.swift | 15 +++++++++++ Tests/SwiftSitemapTests/Utils/Text.swift | 19 +++++++++++++ .../Utils/XCTAssertDocument.swift | 20 ++++++++++++++ Tests/SwiftSitemapTests/assert.swift | 17 ------------ Tests/SwiftSvgTests/Utils/String+Minify.swift | 15 +++++++++++ Tests/SwiftSvgTests/Utils/Text.swift | 19 +++++++++++++ .../Utils/XCTAssertDocument.swift | 20 ++++++++++++++ Tests/SwiftSvgTests/assert.swift | 17 ------------ 25 files changed, 226 insertions(+), 99 deletions(-) create mode 100644 Tests/SwiftHtmlTests/Utils/String+Minify.swift rename Tests/{SwiftSgmlTests => SwiftHtmlTests}/Utils/Text.swift (100%) create mode 100644 Tests/SwiftHtmlTests/Utils/XCTAssertDocument.swift delete mode 100644 Tests/SwiftHtmlTests/assert.swift create mode 100644 Tests/SwiftRssTests/Utils/String+Minify.swift create mode 100644 Tests/SwiftRssTests/Utils/Text.swift create mode 100644 Tests/SwiftRssTests/Utils/XCTAssertDocument.swift delete mode 100644 Tests/SwiftRssTests/assert.swift rename {Sources/SwiftSgml => Tests/SwiftSgmlTests/Utils}/Attributes/Custom.swift (94%) rename {Sources/SwiftSgml => Tests/SwiftSgmlTests/Utils}/Attributes/Flag.swift (93%) rename Tests/SwiftSgmlTests/Utils/{ => Elements}/Branch.swift (100%) rename Tests/SwiftSgmlTests/Utils/{ => Elements}/Leaf.swift (100%) rename Tests/SwiftSgmlTests/Utils/{ => Elements}/Root.swift (100%) create mode 100644 Tests/SwiftSgmlTests/Utils/Elements/Text.swift create mode 100644 Tests/SwiftSitemapTests/Utils/String+Minify.swift create mode 100644 Tests/SwiftSitemapTests/Utils/Text.swift create mode 100644 Tests/SwiftSitemapTests/Utils/XCTAssertDocument.swift delete mode 100644 Tests/SwiftSitemapTests/assert.swift create mode 100644 Tests/SwiftSvgTests/Utils/String+Minify.swift create mode 100644 Tests/SwiftSvgTests/Utils/Text.swift create mode 100644 Tests/SwiftSvgTests/Utils/XCTAssertDocument.swift delete mode 100644 Tests/SwiftSvgTests/assert.swift diff --git a/Makefile b/Makefile index 53f8dfa..04dc703 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ test: - swift test --enable-test-discovery --parallel + swift test --parallel diff --git a/Sources/SwiftSgml/Builder.swift b/Sources/SwiftSgml/Builder.swift index 75259b6..ddaa191 100644 --- a/Sources/SwiftSgml/Builder.swift +++ b/Sources/SwiftSgml/Builder.swift @@ -25,14 +25,6 @@ public enum Builder { public static func buildBlock(_ components: T...) -> [T] { components } - -// public static func buildBlock(_ components: T) -> [T] { -// [components] -// } - -// public static func buildBlock(_ components: [T]...) -> [T] { -// components.flatMap { $0 } -// } public static func buildEither(first component: T) -> T { component @@ -43,26 +35,7 @@ public enum Builder { } } -extension [SwiftSgml.Element]: SwiftSgml.Element { - - public var node: Node { - .group(map { $0.node }) - } -} - extension Builder where T == Element { - -// public static func buildBlock(_ components: T) -> [T] { -// [components] -// } - -// public static func buildExpression(_ expression: [T]) -> T { -// Group(expression) -// } - -// public static func buildExpression(_ expression: Void) -> T { -// Group([]) -// } public static func buildBlock(_ components: T...) -> T { Group(components) diff --git a/Sources/SwiftSgml/Interfaces/Element.swift b/Sources/SwiftSgml/Interfaces/Element.swift index d8820f3..abc3bbc 100644 --- a/Sources/SwiftSgml/Interfaces/Element.swift +++ b/Sources/SwiftSgml/Interfaces/Element.swift @@ -18,7 +18,9 @@ extension Element { } } +extension [SwiftSgml.Element]: SwiftSgml.Element { - - - + public var node: Node { + .group(map { $0.node }) + } +} diff --git a/Tests/SwiftHtmlTests/Utils/String+Minify.swift b/Tests/SwiftHtmlTests/Utils/String+Minify.swift new file mode 100644 index 0000000..ffacc43 --- /dev/null +++ b/Tests/SwiftHtmlTests/Utils/String+Minify.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +extension String { + + func minify() -> String { + self + .replacingOccurrences(of: " ", with: "") + .replacingOccurrences(of: "\n", with: "") + } +} diff --git a/Tests/SwiftSgmlTests/Utils/Text.swift b/Tests/SwiftHtmlTests/Utils/Text.swift similarity index 100% rename from Tests/SwiftSgmlTests/Utils/Text.swift rename to Tests/SwiftHtmlTests/Utils/Text.swift diff --git a/Tests/SwiftHtmlTests/Utils/XCTAssertDocument.swift b/Tests/SwiftHtmlTests/Utils/XCTAssertDocument.swift new file mode 100644 index 0000000..009098d --- /dev/null +++ b/Tests/SwiftHtmlTests/Utils/XCTAssertDocument.swift @@ -0,0 +1,20 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import XCTest +import SwiftSgml + +func XCTAssertDocument( + _ document: Document, + _ expectation: String, + file: StaticString = #filePath, + line: UInt = #line +) { + let renderer = DocumentRenderer() + let html = renderer.render(document) + XCTAssertEqual(html, expectation.minify(), file: file, line: line) +} diff --git a/Tests/SwiftHtmlTests/assert.swift b/Tests/SwiftHtmlTests/assert.swift deleted file mode 100644 index fcb75c4..0000000 --- a/Tests/SwiftHtmlTests/assert.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// assert.swift -// SwiftSgmlTests -// -// Created by Tibor Bodecs on 2023. 04. 01.. -// - -import SwiftSgml -import XCTest - -func assert(doc: Document, html: String) { - let renderer = DocumentRenderer(minify: true) - let expectation = html - .replacingOccurrences(of: " ", with: "") - .replacingOccurrences(of: "\n", with: "") - XCTAssertEqual(renderer.render(doc), expectation) -} diff --git a/Tests/SwiftRssTests/Utils/String+Minify.swift b/Tests/SwiftRssTests/Utils/String+Minify.swift new file mode 100644 index 0000000..ffacc43 --- /dev/null +++ b/Tests/SwiftRssTests/Utils/String+Minify.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +extension String { + + func minify() -> String { + self + .replacingOccurrences(of: " ", with: "") + .replacingOccurrences(of: "\n", with: "") + } +} diff --git a/Tests/SwiftRssTests/Utils/Text.swift b/Tests/SwiftRssTests/Utils/Text.swift new file mode 100644 index 0000000..1efebe1 --- /dev/null +++ b/Tests/SwiftRssTests/Utils/Text.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import SwiftSgml + +struct Text: Element { + + let value: String + + init(_ value: String) { + self.value = value + } + + var node: Node { .text(.init(value: value)) } +} diff --git a/Tests/SwiftRssTests/Utils/XCTAssertDocument.swift b/Tests/SwiftRssTests/Utils/XCTAssertDocument.swift new file mode 100644 index 0000000..009098d --- /dev/null +++ b/Tests/SwiftRssTests/Utils/XCTAssertDocument.swift @@ -0,0 +1,20 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import XCTest +import SwiftSgml + +func XCTAssertDocument( + _ document: Document, + _ expectation: String, + file: StaticString = #filePath, + line: UInt = #line +) { + let renderer = DocumentRenderer() + let html = renderer.render(document) + XCTAssertEqual(html, expectation.minify(), file: file, line: line) +} diff --git a/Tests/SwiftRssTests/assert.swift b/Tests/SwiftRssTests/assert.swift deleted file mode 100644 index fcb75c4..0000000 --- a/Tests/SwiftRssTests/assert.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// assert.swift -// SwiftSgmlTests -// -// Created by Tibor Bodecs on 2023. 04. 01.. -// - -import SwiftSgml -import XCTest - -func assert(doc: Document, html: String) { - let renderer = DocumentRenderer(minify: true) - let expectation = html - .replacingOccurrences(of: " ", with: "") - .replacingOccurrences(of: "\n", with: "") - XCTAssertEqual(renderer.render(doc), expectation) -} diff --git a/Sources/SwiftSgml/Attributes/Custom.swift b/Tests/SwiftSgmlTests/Utils/Attributes/Custom.swift similarity index 94% rename from Sources/SwiftSgml/Attributes/Custom.swift rename to Tests/SwiftSgmlTests/Utils/Attributes/Custom.swift index 13958d2..55f68cb 100644 --- a/Sources/SwiftSgml/Attributes/Custom.swift +++ b/Tests/SwiftSgmlTests/Utils/Attributes/Custom.swift @@ -5,6 +5,8 @@ // Created by Tibor Bodecs on 24/02/2024. // +import SwiftSgml + public struct Custom: Attribute { public let key: String diff --git a/Sources/SwiftSgml/Attributes/Flag.swift b/Tests/SwiftSgmlTests/Utils/Attributes/Flag.swift similarity index 93% rename from Sources/SwiftSgml/Attributes/Flag.swift rename to Tests/SwiftSgmlTests/Utils/Attributes/Flag.swift index 3ca0543..4b61777 100644 --- a/Sources/SwiftSgml/Attributes/Flag.swift +++ b/Tests/SwiftSgmlTests/Utils/Attributes/Flag.swift @@ -5,6 +5,8 @@ // Created by Tibor Bodecs on 24/02/2024. // +import SwiftSgml + public struct Flag: Attribute { public let key: String diff --git a/Tests/SwiftSgmlTests/Utils/Branch.swift b/Tests/SwiftSgmlTests/Utils/Elements/Branch.swift similarity index 100% rename from Tests/SwiftSgmlTests/Utils/Branch.swift rename to Tests/SwiftSgmlTests/Utils/Elements/Branch.swift diff --git a/Tests/SwiftSgmlTests/Utils/Leaf.swift b/Tests/SwiftSgmlTests/Utils/Elements/Leaf.swift similarity index 100% rename from Tests/SwiftSgmlTests/Utils/Leaf.swift rename to Tests/SwiftSgmlTests/Utils/Elements/Leaf.swift diff --git a/Tests/SwiftSgmlTests/Utils/Root.swift b/Tests/SwiftSgmlTests/Utils/Elements/Root.swift similarity index 100% rename from Tests/SwiftSgmlTests/Utils/Root.swift rename to Tests/SwiftSgmlTests/Utils/Elements/Root.swift diff --git a/Tests/SwiftSgmlTests/Utils/Elements/Text.swift b/Tests/SwiftSgmlTests/Utils/Elements/Text.swift new file mode 100644 index 0000000..1efebe1 --- /dev/null +++ b/Tests/SwiftSgmlTests/Utils/Elements/Text.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import SwiftSgml + +struct Text: Element { + + let value: String + + init(_ value: String) { + self.value = value + } + + var node: Node { .text(.init(value: value)) } +} diff --git a/Tests/SwiftSitemapTests/Utils/String+Minify.swift b/Tests/SwiftSitemapTests/Utils/String+Minify.swift new file mode 100644 index 0000000..ffacc43 --- /dev/null +++ b/Tests/SwiftSitemapTests/Utils/String+Minify.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +extension String { + + func minify() -> String { + self + .replacingOccurrences(of: " ", with: "") + .replacingOccurrences(of: "\n", with: "") + } +} diff --git a/Tests/SwiftSitemapTests/Utils/Text.swift b/Tests/SwiftSitemapTests/Utils/Text.swift new file mode 100644 index 0000000..1efebe1 --- /dev/null +++ b/Tests/SwiftSitemapTests/Utils/Text.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import SwiftSgml + +struct Text: Element { + + let value: String + + init(_ value: String) { + self.value = value + } + + var node: Node { .text(.init(value: value)) } +} diff --git a/Tests/SwiftSitemapTests/Utils/XCTAssertDocument.swift b/Tests/SwiftSitemapTests/Utils/XCTAssertDocument.swift new file mode 100644 index 0000000..009098d --- /dev/null +++ b/Tests/SwiftSitemapTests/Utils/XCTAssertDocument.swift @@ -0,0 +1,20 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import XCTest +import SwiftSgml + +func XCTAssertDocument( + _ document: Document, + _ expectation: String, + file: StaticString = #filePath, + line: UInt = #line +) { + let renderer = DocumentRenderer() + let html = renderer.render(document) + XCTAssertEqual(html, expectation.minify(), file: file, line: line) +} diff --git a/Tests/SwiftSitemapTests/assert.swift b/Tests/SwiftSitemapTests/assert.swift deleted file mode 100644 index fcb75c4..0000000 --- a/Tests/SwiftSitemapTests/assert.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// assert.swift -// SwiftSgmlTests -// -// Created by Tibor Bodecs on 2023. 04. 01.. -// - -import SwiftSgml -import XCTest - -func assert(doc: Document, html: String) { - let renderer = DocumentRenderer(minify: true) - let expectation = html - .replacingOccurrences(of: " ", with: "") - .replacingOccurrences(of: "\n", with: "") - XCTAssertEqual(renderer.render(doc), expectation) -} diff --git a/Tests/SwiftSvgTests/Utils/String+Minify.swift b/Tests/SwiftSvgTests/Utils/String+Minify.swift new file mode 100644 index 0000000..ffacc43 --- /dev/null +++ b/Tests/SwiftSvgTests/Utils/String+Minify.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +extension String { + + func minify() -> String { + self + .replacingOccurrences(of: " ", with: "") + .replacingOccurrences(of: "\n", with: "") + } +} diff --git a/Tests/SwiftSvgTests/Utils/Text.swift b/Tests/SwiftSvgTests/Utils/Text.swift new file mode 100644 index 0000000..1efebe1 --- /dev/null +++ b/Tests/SwiftSvgTests/Utils/Text.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import SwiftSgml + +struct Text: Element { + + let value: String + + init(_ value: String) { + self.value = value + } + + var node: Node { .text(.init(value: value)) } +} diff --git a/Tests/SwiftSvgTests/Utils/XCTAssertDocument.swift b/Tests/SwiftSvgTests/Utils/XCTAssertDocument.swift new file mode 100644 index 0000000..009098d --- /dev/null +++ b/Tests/SwiftSvgTests/Utils/XCTAssertDocument.swift @@ -0,0 +1,20 @@ +// +// File.swift +// +// +// Created by Tibor Bodecs on 24/02/2024. +// + +import XCTest +import SwiftSgml + +func XCTAssertDocument( + _ document: Document, + _ expectation: String, + file: StaticString = #filePath, + line: UInt = #line +) { + let renderer = DocumentRenderer() + let html = renderer.render(document) + XCTAssertEqual(html, expectation.minify(), file: file, line: line) +} diff --git a/Tests/SwiftSvgTests/assert.swift b/Tests/SwiftSvgTests/assert.swift deleted file mode 100644 index fcb75c4..0000000 --- a/Tests/SwiftSvgTests/assert.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// assert.swift -// SwiftSgmlTests -// -// Created by Tibor Bodecs on 2023. 04. 01.. -// - -import SwiftSgml -import XCTest - -func assert(doc: Document, html: String) { - let renderer = DocumentRenderer(minify: true) - let expectation = html - .replacingOccurrences(of: " ", with: "") - .replacingOccurrences(of: "\n", with: "") - XCTAssertEqual(renderer.render(doc), expectation) -} From d328487a3f119fcb2c6668cafeca283a058323e2 Mon Sep 17 00:00:00 2001 From: Tibor Bodecs Date: Sat, 24 Feb 2024 23:07:00 +0100 Subject: [PATCH 09/48] add scripts --- .mailmap | 4 + .swift-format | 64 ++++ CONTRIBUTORS.txt | 11 + Makefile | 12 +- Package.swift | 90 +++--- Sources/SwiftHtml/Attributes/Events.swift | 202 ++++++------ Sources/SwiftHtml/Attributes/Global.swift | 142 +++++---- Sources/SwiftHtml/Attributes/Target.swift | 2 +- Sources/SwiftHtml/Elements/A.swift | 52 ++-- Sources/SwiftHtml/Elements/Abbr.swift | 1 - Sources/SwiftHtml/Elements/Area.swift | 66 ++-- Sources/SwiftHtml/Elements/Article.swift | 5 +- Sources/SwiftHtml/Elements/Aside.swift | 9 +- Sources/SwiftHtml/Elements/Audio.swift | 33 +- Sources/SwiftHtml/Elements/B.swift | 2 +- Sources/SwiftHtml/Elements/Base.swift | 7 +- Sources/SwiftHtml/Elements/Bdi.swift | 2 +- Sources/SwiftHtml/Elements/Blockquote.swift | 6 +- Sources/SwiftHtml/Elements/Body.swift | 4 +- Sources/SwiftHtml/Elements/Br.swift | 7 +- Sources/SwiftHtml/Elements/Button.swift | 52 ++-- Sources/SwiftHtml/Elements/Canvas.swift | 14 +- Sources/SwiftHtml/Elements/Caption.swift | 4 +- Sources/SwiftHtml/Elements/Cite.swift | 6 +- Sources/SwiftHtml/Elements/Col.swift | 10 +- Sources/SwiftHtml/Elements/Colgroup.swift | 6 +- Sources/SwiftHtml/Elements/Comment.swift | 2 +- Sources/SwiftHtml/Elements/Data.swift | 8 +- Sources/SwiftHtml/Elements/Datalist.swift | 2 +- Sources/SwiftHtml/Elements/Dd.swift | 4 +- Sources/SwiftHtml/Elements/Del.swift | 12 +- Sources/SwiftHtml/Elements/Details.swift | 7 +- Sources/SwiftHtml/Elements/Dfn.swift | 2 +- Sources/SwiftHtml/Elements/Dialog.swift | 8 +- Sources/SwiftHtml/Elements/Div.swift | 3 - Sources/SwiftHtml/Elements/Dt.swift | 4 +- Sources/SwiftHtml/Elements/Embed.swift | 17 +- Sources/SwiftHtml/Elements/Fieldset.swift | 18 +- Sources/SwiftHtml/Elements/Figcaption.swift | 2 +- Sources/SwiftHtml/Elements/Figure.swift | 1 - Sources/SwiftHtml/Elements/Footer.swift | 2 +- Sources/SwiftHtml/Elements/Form.swift | 44 +-- Sources/SwiftHtml/Elements/H2.swift | 2 +- Sources/SwiftHtml/Elements/H4.swift | 2 +- Sources/SwiftHtml/Elements/H5.swift | 2 +- Sources/SwiftHtml/Elements/Header.swift | 3 +- Sources/SwiftHtml/Elements/Hr.swift | 2 +- Sources/SwiftHtml/Elements/Html.swift | 6 +- Sources/SwiftHtml/Elements/I.swift | 2 +- Sources/SwiftHtml/Elements/Iframe.swift | 50 +-- Sources/SwiftHtml/Elements/Img.swift | 51 ++- Sources/SwiftHtml/Elements/Input.swift | 132 ++++---- Sources/SwiftHtml/Elements/Ins.swift | 2 +- Sources/SwiftHtml/Elements/Kbd.swift | 2 +- Sources/SwiftHtml/Elements/Label.swift | 10 +- Sources/SwiftHtml/Elements/Li.swift | 9 +- Sources/SwiftHtml/Elements/Link.swift | 64 ++-- Sources/SwiftHtml/Elements/MainTag.swift | 4 +- Sources/SwiftHtml/Elements/Map.swift | 8 +- Sources/SwiftHtml/Elements/Mark.swift | 2 +- Sources/SwiftHtml/Elements/Meta.swift | 45 +-- Sources/SwiftHtml/Elements/Meter.swift | 43 ++- Sources/SwiftHtml/Elements/Nav.swift | 7 +- Sources/SwiftHtml/Elements/Noscript.swift | 2 +- Sources/SwiftHtml/Elements/Object.swift | 36 +-- Sources/SwiftHtml/Elements/Ol.swift | 24 +- Sources/SwiftHtml/Elements/Optgroup.swift | 14 +- Sources/SwiftHtml/Elements/Option.swift | 24 +- Sources/SwiftHtml/Elements/Output.swift | 16 +- Sources/SwiftHtml/Elements/P.swift | 6 +- Sources/SwiftHtml/Elements/Param.swift | 10 +- Sources/SwiftHtml/Elements/Picture.swift | 2 +- Sources/SwiftHtml/Elements/Progress.swift | 12 +- Sources/SwiftHtml/Elements/Q.swift | 6 +- Sources/SwiftHtml/Elements/Rp.swift | 2 +- Sources/SwiftHtml/Elements/Ruby.swift | 2 +- Sources/SwiftHtml/Elements/S.swift | 2 +- Sources/SwiftHtml/Elements/Script.swift | 41 ++- Sources/SwiftHtml/Elements/Select.swift | 32 +- Sources/SwiftHtml/Elements/Small.swift | 2 +- Sources/SwiftHtml/Elements/Source.swift | 35 +-- Sources/SwiftHtml/Elements/Span.swift | 2 +- Sources/SwiftHtml/Elements/Style.swift | 19 +- Sources/SwiftHtml/Elements/Sub.swift | 2 +- Sources/SwiftHtml/Elements/Summary.swift | 2 +- Sources/SwiftHtml/Elements/Sup.swift | 2 +- Sources/SwiftHtml/Elements/Table.swift | 6 +- Sources/SwiftHtml/Elements/Tbody.swift | 3 +- Sources/SwiftHtml/Elements/Td.swift | 6 +- Sources/SwiftHtml/Elements/Template.swift | 3 - Sources/SwiftHtml/Elements/Textarea.swift | 50 +-- Sources/SwiftHtml/Elements/Th.swift | 30 +- Sources/SwiftHtml/Elements/Time.swift | 6 +- Sources/SwiftHtml/Elements/Track.swift | 28 +- Sources/SwiftHtml/Elements/Ul.swift | 6 +- Sources/SwiftHtml/Elements/Var.swift | 2 +- Sources/SwiftHtml/Elements/Video.swift | 44 ++- Sources/SwiftHtml/Exported.swift | 8 - Sources/SwiftHtml/MediaQuery.swift | 30 +- Sources/SwiftHtml/Text.swift | 6 +- Sources/SwiftHtml/v2/Attributes/Class.swift | 2 +- Sources/SwiftHtml/v2/Attributes/Lang.swift | 4 +- Sources/SwiftHtml/v2/Elements/Body.swift | 4 +- Sources/SwiftHtml/v2/Elements/Br.swift | 4 +- Sources/SwiftHtml/v2/Elements/Comment.swift | 6 +- Sources/SwiftHtml/v2/Elements/Head.swift | 4 +- Sources/SwiftHtml/v2/Elements/Html.swift | 8 +- Sources/SwiftHtml/v2/Elements/P.swift | 15 +- Sources/SwiftHtml/v2/Elements/Title.swift | 4 +- Sources/SwiftRss/Channel.swift | 2 +- Sources/SwiftRss/Description.swift | 2 +- Sources/SwiftRss/Exported.swift | 8 - Sources/SwiftRss/Guid.swift | 6 +- Sources/SwiftRss/Item.swift | 4 +- Sources/SwiftRss/LastBuildDate.swift | 1 - Sources/SwiftRss/PubDate.swift | 2 +- Sources/SwiftRss/Rss.swift | 6 +- Sources/SwiftSgml/Builder.swift | 19 +- .../Document/Document+Renderer.swift | 32 +- Sources/SwiftSgml/Document/Document.swift | 6 +- Sources/SwiftSgml/Interfaces/Attribute.swift | 2 +- Sources/SwiftSgml/Interfaces/Attributes.swift | 35 +-- .../SwiftSgml/Interfaces/Element+Parent.swift | 25 +- .../SwiftSgml/Interfaces/Element+Short.swift | 3 +- Sources/SwiftSgml/Interfaces/Element.swift | 2 +- Sources/SwiftSgml/Interfaces/Mutable.swift | 2 +- Sources/SwiftSgml/Node/Node+Comment.swift | 4 +- Sources/SwiftSgml/Node/Node+Short.swift | 4 +- Sources/SwiftSgml/Node/Node+Standard.swift | 4 +- Sources/SwiftSgml/Node/Node+Text.swift | 4 +- Sources/SwiftSgml/Node/Node.swift | 2 +- Sources/SwiftSitemap/ChangeFreq.swift | 4 +- Sources/SwiftSitemap/Exported.swift | 8 - Sources/SwiftSitemap/LastMod.swift | 1 - Sources/SwiftSitemap/Loc.swift | 2 +- Sources/SwiftSitemap/SitemapIndex.swift | 5 +- Sources/SwiftSitemap/UrlSet.swift | 5 +- Sources/SwiftSvg/Circle.swift | 10 +- Sources/SwiftSvg/Double+PreciseString.swift | 7 +- Sources/SwiftSvg/Ellipse.swift | 12 +- Sources/SwiftSvg/Exported.swift | 8 - Sources/SwiftSvg/G.swift | 8 +- Sources/SwiftSvg/Line.swift | 12 +- Sources/SwiftSvg/Path.swift | 3 +- Sources/SwiftSvg/Polygon.swift | 5 +- Sources/SwiftSvg/Polyline.swift | 5 +- Sources/SwiftSvg/Rect.swift | 16 +- Sources/SwiftSvg/Svg.swift | 35 ++- Sources/SwiftSvg/Text.swift | 19 +- Tests/SwiftHtmlTests/SwiftHtmlTests.swift | 120 +++---- .../SwiftHtmlTests/TagCompositionTests.swift | 122 ++++---- Tests/SwiftHtmlTests/Tags/ATagTests.swift | 19 +- Tests/SwiftHtmlTests/Tags/BrTagTests.swift | 10 +- .../SwiftHtmlTests/Tags/CommentTagTests.swift | 10 +- Tests/SwiftHtmlTests/Tags/FormTagTests.swift | 26 +- Tests/SwiftHtmlTests/Tags/H1TagTests.swift | 29 +- Tests/SwiftHtmlTests/Tags/InputTagTests.swift | 62 ++-- Tests/SwiftHtmlTests/Tags/LabelTagTests.swift | 30 +- Tests/SwiftHtmlTests/Tags/LinkTagTests.swift | 38 ++- Tests/SwiftHtmlTests/Tags/MetaTagTests.swift | 37 ++- Tests/SwiftHtmlTests/Tags/PTagTests.swift | 17 +- .../SwiftHtmlTests/Tags/ScriptTagTests.swift | 29 +- .../SwiftHtmlTests/Tags/SourceTagTests.swift | 15 +- Tests/SwiftHtmlTests/Tags/TableTagTests.swift | 71 +++-- .../Tags/TextareaTagTests.swift | 10 +- Tests/SwiftHtmlTests/Tags/UlTagTests.swift | 25 +- .../SwiftHtmlTests/Utils/String+Minify.swift | 2 +- Tests/SwiftHtmlTests/Utils/Text.swift | 6 +- .../Utils/XCTAssertDocument.swift | 4 +- Tests/SwiftRssTests/SwiftRssTests.swift | 35 ++- Tests/SwiftRssTests/Utils/String+Minify.swift | 2 +- Tests/SwiftRssTests/Utils/Text.swift | 6 +- .../Utils/XCTAssertDocument.swift | 4 +- Tests/SwiftSgmlTests/AttributeTests.swift | 51 +-- Tests/SwiftSgmlTests/BuilderTests.swift | 128 ++++---- Tests/SwiftSgmlTests/DocumentTests.swift | 8 +- Tests/SwiftSgmlTests/ElementTests.swift | 47 ++- .../Utils/Attributes/Custom.swift | 5 +- .../Utils/Attributes/Flag.swift | 4 +- .../Utils/Elements/Branch.swift | 4 +- .../SwiftSgmlTests/Utils/Elements/Leaf.swift | 7 +- .../SwiftSgmlTests/Utils/Elements/Root.swift | 23 +- .../SwiftSgmlTests/Utils/Elements/Text.swift | 6 +- .../SwiftSgmlTests/Utils/String+Minify.swift | 2 +- .../Utils/XCTAssertDocument.swift | 4 +- .../SwiftSitemapTests/SwiftSitemapTests.swift | 21 +- .../Utils/String+Minify.swift | 2 +- Tests/SwiftSitemapTests/Utils/Text.swift | 6 +- .../Utils/XCTAssertDocument.swift | 4 +- Tests/SwiftSvgTests/SwiftSvgTests.swift | 294 +++++++++--------- Tests/SwiftSvgTests/Utils/String+Minify.swift | 2 +- Tests/SwiftSvgTests/Utils/Text.swift | 6 +- .../Utils/XCTAssertDocument.swift | 4 +- scripts/check-broken-symlinks.sh | 24 ++ scripts/check-unacceptable-language.sh | 24 ++ scripts/generate-contributors-list.sh | 16 + scripts/install-swift-format.sh | 19 ++ scripts/run-checks.sh | 42 +++ scripts/run-chmod.sh | 10 + scripts/run-clean.sh | 12 + scripts/run-swift-format.sh | 36 +++ scripts/unacceptable-language.txt | 15 + 202 files changed, 2130 insertions(+), 1736 deletions(-) create mode 100644 .mailmap create mode 100644 .swift-format create mode 100644 CONTRIBUTORS.txt delete mode 100644 Sources/SwiftHtml/Exported.swift delete mode 100644 Sources/SwiftRss/Exported.swift delete mode 100644 Sources/SwiftSitemap/Exported.swift delete mode 100644 Sources/SwiftSvg/Exported.swift create mode 100755 scripts/check-broken-symlinks.sh create mode 100755 scripts/check-unacceptable-language.sh create mode 100755 scripts/generate-contributors-list.sh create mode 100755 scripts/install-swift-format.sh create mode 100755 scripts/run-checks.sh create mode 100755 scripts/run-chmod.sh create mode 100755 scripts/run-clean.sh create mode 100755 scripts/run-swift-format.sh create mode 100755 scripts/unacceptable-language.txt diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..cc6dcce --- /dev/null +++ b/.mailmap @@ -0,0 +1,4 @@ +Tibor Bödecs Tibor Bodecs +László Kovács mzpx +Ferenc Viasz-Kádi Ferenc Viasz-Kadi +Gábor Lengyel Gabor Lengyel \ No newline at end of file diff --git a/.swift-format b/.swift-format new file mode 100644 index 0000000..58d21f0 --- /dev/null +++ b/.swift-format @@ -0,0 +1,64 @@ +{ + "version": 1, + "lineLength": 80, + "maximumBlankLines": 1, + "fileScopedDeclarationPrivacy": { + "accessLevel": "private" + }, + "tabWidth": 4, + "indentation": { + "spaces": 4 + }, + "indentConditionalCompilationBlocks": false, + "indentSwitchCaseLabels": false, + "lineBreakAroundMultilineExpressionChainComponents": true, + "lineBreakBeforeControlFlowKeywords": true, + "lineBreakBeforeEachArgument": true, + "lineBreakBeforeEachGenericRequirement": true, + "prioritizeKeepingFunctionOutputTogether": false, + "respectsExistingLineBreaks": true, + "spacesAroundRangeFormationOperators": false, + "multiElementCollectionTrailingCommas": true, + "rules": { + "AllPublicDeclarationsHaveDocumentation": false, + "AlwaysUseLiteralForEmptyCollectionInit": true, + "AlwaysUseLowerCamelCase": true, + "AmbiguousTrailingClosureOverload": true, + "BeginDocumentationCommentWithOneLineSummary": true, + "DoNotUseSemicolons": true, + "DontRepeatTypeInStaticProperties": true, + "FileScopedDeclarationPrivacy": true, + "FullyIndirectEnum": true, + "GroupNumericLiterals": true, + "IdentifiersMustBeASCII": true, + "NeverForceUnwrap": false, + "NeverUseForceTry": true, + "NeverUseImplicitlyUnwrappedOptionals": true, + "NoAccessLevelOnExtensionDeclaration": true, + "NoAssignmentInExpressions": true, + "NoBlockComments": true, + "NoCasesWithOnlyFallthrough": true, + "NoEmptyTrailingClosureParentheses": true, + "NoLabelsInCasePatterns": true, + "NoLeadingUnderscores": true, + "NoParensAroundConditions": true, + "NoPlaygroundLiterals": true, + "NoVoidReturnOnFunctionSignature": true, + "OmitExplicitReturns": true, + "OneCasePerLine": true, + "OneVariableDeclarationPerLine": true, + "OnlyOneTrailingClosureArgument": true, + "OrderedImports": true, + "ReplaceForEachWithForLoop": true, + "ReturnVoidInsteadOfEmptyTuple": true, + "TypeNamesShouldBeCapitalized": true, + "UseEarlyExits": true, + "UseLetInEveryBoundCaseVariable": true, + "UseShorthandTypeNames": true, + "UseSingleLinePropertyGetter": true, + "UseSynthesizedInitializer": true, + "UseTripleSlashForDocumentationComments": true, + "UseWhereClausesInForLoops": true, + "ValidateDocumentationComments": true + } +} diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 0000000..9c405e8 --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,11 @@ +### Contributors + +- Ferenc Viasz-Kádi +- Gábor Lengyel +- László Kovács +- Tibor Bödecs + +**Updating this list** + +Please do not edit this file manually. It is generated using `bash ./scripts/generate-contributors-list.sh`. +If a name is misspelled or appearing multiple times: add an entry in `./.mailmap`. diff --git a/Makefile b/Makefile index 04dc703..839fca2 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,10 @@ -test: - swift test --parallel +SHELL=/bin/bash +# ---------------------------------------- +# Main +# ---------------------------------------- + +check: + ./scripts/run-checks.sh + +format: + ./scripts/run-swift-format.sh --fix diff --git a/Package.swift b/Package.swift index 5ca2250..61eef4b 100644 --- a/Package.swift +++ b/Package.swift @@ -4,49 +4,59 @@ import PackageDescription let package = Package( name: "swift-html", platforms: [ - .macOS(.v10_15), + .macOS(.v13), + .iOS(.v16), + .tvOS(.v16), + .watchOS(.v9), + .visionOS(.v1), ], products: [ - .library(name: "SwiftSgml", targets: ["SwiftSgml"]), -// .library(name: "SwiftHtml", targets: ["SwiftHtml"]), -// .library(name: "SwiftSvg", targets: ["SwiftSvg"]), -// .library(name: "SwiftSitemap", targets: ["SwiftSitemap"]), -// .library(name: "SwiftRss", targets: ["SwiftRss"]), - ], - dependencies: [ - + .library(name: "SwiftSgml", targets: ["SwiftSgml"]) + // .library(name: "SwiftSitemap", targets: ["SwiftSitemap"]), + // .library(name: "SwiftRss", targets: ["SwiftRss"]), + // .library(name: "SwiftHtml", targets: ["SwiftHtml"]), + // .library(name: "SwiftSvg", targets: ["SwiftSvg"]), ], targets: [ - .target(name: "SwiftSgml", dependencies: []), -// .target(name: "SwiftHtml", dependencies: [ -// .target(name: "SwiftSgml") -// ]), -// .target(name: "SwiftSvg", dependencies: [ -// .target(name: "SwiftSgml") -// ]), -// .target(name: "SwiftSitemap", dependencies: [ -// .target(name: "SwiftSgml") -// ]), -// .target(name: "SwiftRss", dependencies: [ -// .target(name: "SwiftSgml") -// ]), -// - .testTarget(name: "SwiftSgmlTests", dependencies: [ - .target(name: "SwiftSgml"), - ]), -// .testTarget(name: "SwiftHtmlTests", dependencies: [ -// .target(name: "SwiftHtml"), -// ]), -// .testTarget(name: "SwiftSvgTests", dependencies: [ -// .target(name: "SwiftSvg"), -// ]), -// .testTarget(name: "SwiftSitemapTests", dependencies: [ -// .target(name: "SwiftSitemap"), -// ]), -// .testTarget(name: "SwiftRssTests", dependencies: [ -// .target(name: "SwiftRss"), -// ]), - ] -) + .target(name: "SwiftSgml"), + .target( + name: "SwiftSitemap", + dependencies: [ + .target(name: "SwiftSgml") + ] + ), + // .target(name: "SwiftRss", dependencies: [ + // .target(name: "SwiftSgml") + // ]), + // .target(name: "SwiftHtml", dependencies: [ + // .target(name: "SwiftSgml") + // ]), + // .target(name: "SwiftSvg", dependencies: [ + // .target(name: "SwiftSgml") + // ]), + // + .testTarget( + name: "SwiftSgmlTests", + dependencies: [ + .target(name: "SwiftSgml") + ] + ), + .testTarget( + name: "SwiftSitemapTests", + dependencies: [ + .target(name: "SwiftSitemap") + ] + ), + // .testTarget(name: "SwiftRssTests", dependencies: [ + // .target(name: "SwiftRss"), + // ]), + // .testTarget(name: "SwiftHtmlTests", dependencies: [ + // .target(name: "SwiftHtml"), + // ]), + // .testTarget(name: "SwiftSvgTests", dependencies: [ + // .target(name: "SwiftSvg"), + // ]), + ] +) diff --git a/Sources/SwiftHtml/Attributes/Events.swift b/Sources/SwiftHtml/Attributes/Events.swift index 28758d5..e58ad12 100644 --- a/Sources/SwiftHtml/Attributes/Events.swift +++ b/Sources/SwiftHtml/Attributes/Events.swift @@ -5,378 +5,376 @@ // Created by Tibor Bodecs on 2021. 07. 23.. // -public extension Tag { - +extension Tag { + // MARK: - Window Event Attributes - + /// Script to be run after the document is printed - func onAfterPrint(_ value: String) -> Self { + public func onAfterPrint(_ value: String) -> Self { attribute("onafterprint", value) } - + /// Script to be run before the document is printed - func onBeforePrint(_ value: String) -> Self { + public func onBeforePrint(_ value: String) -> Self { attribute("onbeforeprint", value) } - + /// Script to be run when the document is about to be unloaded - func onBeforeUnload(_ value: String) -> Self { + public func onBeforeUnload(_ value: String) -> Self { attribute("onbeforeunload", value) } - + /// Script to be run when an error occurs - func onError(_ value: String) -> Self { + public func onError(_ value: String) -> Self { attribute("onerror", value) } - + /// Script to be run when there has been changes to the anchor part of the a URL - func onHashChange(_ value: String) -> Self { + public func onHashChange(_ value: String) -> Self { attribute("onhashchange", value) } - + /// Fires after the page is finished loading - func onLoad(_ value: String) -> Self { + public func onLoad(_ value: String) -> Self { attribute("onload", value) } - + /// Script to be run when the message is triggered - func onMessage(_ value: String) -> Self { + public func onMessage(_ value: String) -> Self { attribute("onmessage", value) } - + /// Script to be run when the browser starts to work offline - func onOffline(_ value: String) -> Self { + public func onOffline(_ value: String) -> Self { attribute("onoffline", value) } - + /// Script to be run when the browser starts to work online - func onOnline(_ value: String) -> Self { + public func onOnline(_ value: String) -> Self { attribute("ononline", value) } - + /// Script to be run when a user navigates away from a page - func onPageHide(_ value: String) -> Self { + public func onPageHide(_ value: String) -> Self { attribute("onpagehide", value) } - + /// Script to be run when a user navigates to a page - func onPageShow(_ value: String) -> Self { + public func onPageShow(_ value: String) -> Self { attribute("onpageshow", value) } - + /// Script to be run when the window's history changes - func onPopState(_ value: String) -> Self { + public func onPopState(_ value: String) -> Self { attribute("onpopstate", value) } - + /// Fires when the browser window is resized - func onResize(_ value: String) -> Self { + public func onResize(_ value: String) -> Self { attribute("onresize", value) } - + /// Script to be run when a Web Storage area is updated - func onStorage(_ value: String) -> Self { + public func onStorage(_ value: String) -> Self { attribute("onstorage", value) } - + /// Fires once a page has unloaded (or the browser window has been closed) - func onUnload(_ value: String) -> Self { + public func onUnload(_ value: String) -> Self { attribute("onunload", value) } - + // MARK: - Form Events - + /// Fires the moment that the element loses focus - func onBlur(_ value: String) -> Self { + public func onBlur(_ value: String) -> Self { attribute("onblur", value) } /// Fires the moment when the value of the element is changed - func onChange(_ value: String) -> Self { + public func onChange(_ value: String) -> Self { attribute("onchange", value) } /// Script to be run when a context menu is triggered - func onContextMenu(_ value: String) -> Self { + public func onContextMenu(_ value: String) -> Self { attribute("oncontextmenu", value) } /// Fires the moment when the element gets focus - func onFocus(_ value: String) -> Self { + public func onFocus(_ value: String) -> Self { attribute("onfocus", value) } /// Script to be run when an element gets user input - func onInput(_ value: String) -> Self { + public func onInput(_ value: String) -> Self { attribute("oninput", value) } /// Script to be run when an element is invalid - func onInvalid(_ value: String) -> Self { + public func onInvalid(_ value: String) -> Self { attribute("oninvalid", value) } /// Fires when the Reset button in a form is clicked - func onReset(_ value: String) -> Self { + public func onReset(_ value: String) -> Self { attribute("onreset", value) } /// Fires when the user writes something in a search field (for ) - func onSearch(_ value: String) -> Self { + public func onSearch(_ value: String) -> Self { attribute("onsearch", value) } /// Fires after some text has been selected in an element - func onSelect(_ value: String) -> Self { + public func onSelect(_ value: String) -> Self { attribute("onselect", value) } /// Fires when a form is submitted - func onSubmit(_ value: String) -> Self { + public func onSubmit(_ value: String) -> Self { attribute("onsubmit", value) } - + // MARK: - Keyboard Events - /// Fires when a user is pressing a key - func onKeyDown(_ value: String) -> Self { + public func onKeyDown(_ value: String) -> Self { attribute("onkeydown", value) } /// Fires when a user presses a key - func onKeyPress(_ value: String) -> Self { + public func onKeyPress(_ value: String) -> Self { attribute("onkeypress", value) } /// Fires when a user releases a key - func onKeyUp(_ value: String) -> Self { + public func onKeyUp(_ value: String) -> Self { attribute("onkeyup", value) } - + // MARK: - Mouse Events - /// Fires on a mouse click on the element - func onClick(_ value: String) -> Self { + public func onClick(_ value: String) -> Self { attribute("onclick", value) } /// Fires on a mouse double-click on the element - func onDoubleClick(_ value: String) -> Self { + public func onDoubleClick(_ value: String) -> Self { attribute("ondblclick", value) } /// Fires when a mouse button is pressed down on an element - func onMouseDown(_ value: String) -> Self { + public func onMouseDown(_ value: String) -> Self { attribute("onmousedown", value) } /// Fires when the mouse pointer is moving while it is over an element - func onMouseMove(_ value: String) -> Self { + public func onMouseMove(_ value: String) -> Self { attribute("onmousemove", value) } /// Fires when the mouse pointer moves out of an element - func onMouseOut(_ value: String) -> Self { + public func onMouseOut(_ value: String) -> Self { attribute("onmouseout", value) } /// Fires when the mouse pointer moves over an element - func onMouseOver(_ value: String) -> Self { + public func onMouseOver(_ value: String) -> Self { attribute("onmouseover", value) } /// Fires when a mouse button is released over an element - func onMouseUp(_ value: String) -> Self { + public func onMouseUp(_ value: String) -> Self { attribute("onmouseup", value) } /// Fires when the mouse wheel rolls up or down over an element - func onWheel(_ value: String) -> Self { + public func onWheel(_ value: String) -> Self { attribute("onwheel", value) } - + // MARK: - Drag Events /// Script to be run when an element is dragged - func onDrag(_ value: String) -> Self { + public func onDrag(_ value: String) -> Self { attribute("ondrag", value) } /// Script to be run at the end of a drag operation - func onDragEnd(_ value: String) -> Self { + public func onDragEnd(_ value: String) -> Self { attribute("ondragend", value) } /// Script to be run when an element has been dragged to a valid drop target - func onDragEnter(_ value: String) -> Self { + public func onDragEnter(_ value: String) -> Self { attribute("ondragenter", value) } /// Script to be run when an element leaves a valid drop target - func onDragLeave(_ value: String) -> Self { + public func onDragLeave(_ value: String) -> Self { attribute("ondragleave", value) } /// Script to be run when an element is being dragged over a valid drop target - func onDragOver(_ value: String) -> Self { + public func onDragOver(_ value: String) -> Self { attribute("ondragover", value) } /// Script to be run at the start of a drag operation - func onDragStart(_ value: String) -> Self { + public func onDragStart(_ value: String) -> Self { attribute("ondragstart", value) } /// Script to be run when dragged element is being dropped - func onDrop(_ value: String) -> Self { + public func onDrop(_ value: String) -> Self { attribute("ondrop", value) } /// Script to be run when an element's scrollbar is being scrolled - func onScroll(_ value: String) -> Self { + public func onScroll(_ value: String) -> Self { attribute("onscroll", value) } - + // MARK: - Clipboard Events /// Fires when the user copies the content of an element - func onCopy(_ value: String) -> Self { + public func onCopy(_ value: String) -> Self { attribute("oncopy", value) } /// Fires when the user cuts the content of an element - func onCut(_ value: String) -> Self { + public func onCut(_ value: String) -> Self { attribute("oncut", value) } /// Fires when the user pastes some content in an element - func onPaste(_ value: String) -> Self { + public func onPaste(_ value: String) -> Self { attribute("onpaste", value) } - + // MARK: - Media Events - + /// Script to be run on abort - func onAbort(_ value: String) -> Self { + public func onAbort(_ value: String) -> Self { attribute("onabort", value) } /// Script to be run when a file is ready to start playing (when it has buffered enough to begin) - func onCanPlay(_ value: String) -> Self { + public func onCanPlay(_ value: String) -> Self { attribute("oncanplay", value) } /// Script to be run when a file can be played all the way to the end without pausing for buffering - func onCanPlaythrough(_ value: String) -> Self { + public func onCanPlaythrough(_ value: String) -> Self { attribute("oncanplaythrough", value) } /// Script to be run when the cue changes in a element - func onCueChange(_ value: String) -> Self { + public func onCueChange(_ value: String) -> Self { attribute("oncuechange", value) } /// Script to be run when the length of the media changes - func onDurationChange(_ value: String) -> Self { + public func onDurationChange(_ value: String) -> Self { attribute("ondurationchange", value) } /// Script to be run when something bad happens and the file is suddenly unavailable (like unexpectedly disconnects) - func onEmptied(_ value: String) -> Self { + public func onEmptied(_ value: String) -> Self { attribute("onemptied", value) } /// Script to be run when the media has reach the end (a useful event for messages like "thanks for listening") - func onEnded(_ value: String) -> Self { + public func onEnded(_ value: String) -> Self { attribute("onended", value) } /// Script to be run when an error occurs when the file is being loaded -// func onError(_ value: String) -> Self { -// attribute("onerror", value) -// } + // func onError(_ value: String) -> Self { + // attribute("onerror", value) + // } /// Script to be run when media data is loaded - func onLoadedData(_ value: String) -> Self { + public func onLoadedData(_ value: String) -> Self { attribute("onloadeddata", value) } /// Script to be run when meta data (like dimensions and duration) are loaded - func onLoadedMetadata(_ value: String) -> Self { + public func onLoadedMetadata(_ value: String) -> Self { attribute("onloadedmetadata", value) } /// Script to be run just as the file begins to load before anything is actually loaded - func onLoadStart(_ value: String) -> Self { + public func onLoadStart(_ value: String) -> Self { attribute("onloadstart", value) } /// Script to be run when the media is paused either by the user or programmatically - func onPause(_ value: String) -> Self { + public func onPause(_ value: String) -> Self { attribute("onpause", value) } /// Script to be run when the media is ready to start playing - func onPlay(_ value: String) -> Self { + public func onPlay(_ value: String) -> Self { attribute("onplay", value) } /// Script to be run when the media actually has started playing - func onPlaying(_ value: String) -> Self { + public func onPlaying(_ value: String) -> Self { attribute("onplaying", value) } /// Script to be run when the browser is in the process of getting the media data - func onProgress(_ value: String) -> Self { + public func onProgress(_ value: String) -> Self { attribute("onprogress", value) } /// Script to be run each time the playback rate changes (like when a user switches to a slow motion or fast forward mode) - func onRateChange(_ value: String) -> Self { + public func onRateChange(_ value: String) -> Self { attribute("onratechange", value) } /// Script to be run when the seeking attribute is set to false indicating that seeking has ended - func onSeeked(_ value: String) -> Self { + public func onSeeked(_ value: String) -> Self { attribute("onseeked", value) } /// Script to be run when the seeking attribute is set to true indicating that seeking is active - func onSeeking(_ value: String) -> Self { + public func onSeeking(_ value: String) -> Self { attribute("onseeking", value) } /// Script to be run when the browser is unable to fetch the media data for whatever reason - func onStalled(_ value: String) -> Self { + public func onStalled(_ value: String) -> Self { attribute("onstalled", value) } /// Script to be run when fetching the media data is stopped before it is completely loaded for whatever reason - func onSuspend(_ value: String) -> Self { + public func onSuspend(_ value: String) -> Self { attribute("onsuspend", value) } /// Script to be run when the playing position has changed (like when the user fast forwards to a different point in the media) - func onTimeUpdate(_ value: String) -> Self { + public func onTimeUpdate(_ value: String) -> Self { attribute("ontimeupdate", value) } /// Script to be run each time the volume is changed which (includes setting the volume to "mute") - func onVolumeChange(_ value: String) -> Self { + public func onVolumeChange(_ value: String) -> Self { attribute("onvolumechange", value) } /// Script to be run when the media has paused but is expected to resume (like when the media pauses to buffer more data) - func onWaiting(_ value: String) -> Self { + public func onWaiting(_ value: String) -> Self { attribute("onwaiting", value) } - + // MARK: - Misc Events - + /// Fires when the user opens or closes the
element - func onToggle(_ value: String) -> Self { + public func onToggle(_ value: String) -> Self { attribute("ontoggle", value) } } diff --git a/Sources/SwiftHtml/Attributes/Global.swift b/Sources/SwiftHtml/Attributes/Global.swift index b1c5526..6810219 100644 --- a/Sources/SwiftHtml/Attributes/Global.swift +++ b/Sources/SwiftHtml/Attributes/Global.swift @@ -31,32 +31,32 @@ public enum Translate: String { case no } -private extension String { +extension String { /// turns a list of class values (separated by a space" into an array of strings - var classArray: [String] { + fileprivate var classArray: [String] { split(separator: " ").map { String($0) } } - + /// turns a list of style values (separated by a semicolon) into an array of strings - var styleArray: [String] { + fileprivate var styleArray: [String] { split(separator: ";").map { String($0) } } } -private extension Array where Element == String { - - var classString: String { +extension Array where Element == String { + + fileprivate var classString: String { filter { !$0.isEmpty }.joined(separator: " ") } - - var styleString: String { + + fileprivate var styleString: String { filter { !$0.isEmpty }.joined(separator: ";") } } -public extension Tag { - +extension Tag { + // MARK: - class management /// find an existing class attribute and return the value as an array of strings or an empty array @@ -65,37 +65,38 @@ public extension Tag { } /// Specifies one classname for an element (refers to a class in a style sheet) - func `class`(_ value: String?, _ condition: Bool = true) -> Self { + public func `class`(_ value: String?, _ condition: Bool = true) -> Self { attribute("class", value, condition) } /// Specifies multiple classnames for an element (refers to a class in a style sheet) - func `class`(_ values: [String], _ condition: Bool = true) -> Self { + public func `class`(_ values: [String], _ condition: Bool = true) -> Self { /// @NOTE: explicit true flag is needed, otherwise Swift won't know which function to call... `class`(values.classString, condition) } /// Specifies multiple classnames for an element (refers to a class in a style sheet) - func `class`(_ values: String...) -> Self { + public func `class`(_ values: String...) -> Self { `class`(values) } - + /// Adds a single value to the class list if the condition is true /// /// Note: If the value is empty or nil it won't be added to the list /// - func `class`(add value: String?, _ condition: Bool = true) -> Self { + public func `class`(add value: String?, _ condition: Bool = true) -> Self { guard let value = value else { return self } return `class`(add: [value], condition) } - + /// Adds an array of values to the class list if the condition is true /// /// Note: If the value is empty it won't be added to the list /// - func `class`(add values: [String], _ condition: Bool = true) -> Self { + public func `class`(add values: [String], _ condition: Bool = true) -> Self + { let newValues = classArray + values.filter { !$0.isEmpty } var newValue: String? = nil @@ -104,26 +105,30 @@ public extension Tag { } return `class`(newValue, condition) } - + /// Removes a given class values if the condition is true - func `class`(remove value: String?, _ condition: Bool = true) -> Self { + public func `class`(remove value: String?, _ condition: Bool = true) -> Self + { guard let value = value else { return self } return `class`(remove: [value], condition) } - + /// Removes an array of class values if the condition is true - func `class`(remove values: [String], _ condition: Bool = true) -> Self { + public func `class`(remove values: [String], _ condition: Bool = true) + -> Self + { let newClasses = classArray.filter { !values.contains($0) } if newClasses.isEmpty { return deleteAttribute("class") } return `class`(newClasses, condition) } - + /// toggles a single class value - func `class`(toggle value: String?, _ condition: Bool = true) -> Self { + public func `class`(toggle value: String?, _ condition: Bool = true) -> Self + { guard let value = value else { return self } @@ -141,38 +146,38 @@ public extension Tag { } /// Specifies one stylename for an element (refers to a style in a style sheet) - func style(_ value: String?, _ condition: Bool = true) -> Self { + public func style(_ value: String?, _ condition: Bool = true) -> Self { guard let value, !value.isEmpty else { return self } return attribute("style", value, condition) } /// Specifies multiple stylenames for an element (refers to a style in a style sheet) - func style(_ values: [String], _ condition: Bool = true) -> Self { + public func style(_ values: [String], _ condition: Bool = true) -> Self { /// @NOTE: explicit true flag is needed, otherwise Swift won't know which function to call... style(values.styleString, condition) } /// Specifies multiple stylenames for an element (refers to a style in a style sheet) - func style(_ values: String...) -> Self { + public func style(_ values: String...) -> Self { style(values) } - + /// Adds a single value to the style list if the condition is true /// /// Note: If the value is empty or nil it won't be added to the list /// - func style(add value: String?, _ condition: Bool = true) -> Self { + public func style(add value: String?, _ condition: Bool = true) -> Self { guard let value = value else { return self } return style(add: [value], condition) } - + /// Adds an array of values to the style list if the condition is true /// /// Note: If the value is empty it won't be added to the list /// - func style(add values: [String], _ condition: Bool = true) -> Self { + public func style(add values: [String], _ condition: Bool = true) -> Self { let newValues = styleArray + values.filter { !$0.isEmpty } var newValue: String? = nil @@ -181,46 +186,52 @@ public extension Tag { } return style(newValue, condition) } - + /// Removes a given style values if the condition is true - func style(remove value: String?, _ condition: Bool = true) -> Self { + public func style(remove value: String?, _ condition: Bool = true) -> Self { guard let value = value else { return self } return style(remove: [value], condition) } - + /// Removes an array of style values if the condition is true - func style(remove values: [String], _ condition: Bool = true) -> Self { + public func style(remove values: [String], _ condition: Bool = true) -> Self + { let newClasses = styleArray.filter { !values.contains($0) } if newClasses.isEmpty { return deleteAttribute("style") } return style(newClasses, condition) } - + /// Removes a given style value with its key name if the condition is true /// `.style(removeByKey: "font-size")` as opposed to `.style(remove: "font-size: 12rem")` - func style(removeByKey value: String?, _ condition: Bool = true) -> Self { + public func style(removeByKey value: String?, _ condition: Bool = true) + -> Self + { guard let value = value else { return self } return style(removeByKey: [value], condition) } - /// Removes an array of style values with the key name if the condition is true /// `.style(removeByKey:[ "font-size"])` as opposed to `.style(remove: ["font-size: 12rem"])` - func style(removeByKey values: [String], _ condition: Bool = true) -> Self { - let newClasses = styleArray.filter { !values.contains(String($0.prefix(while: {$0 != ":"}))) } + public func style(removeByKey values: [String], _ condition: Bool = true) + -> Self + { + let newClasses = styleArray.filter { + !values.contains(String($0.prefix(while: { $0 != ":" }))) + } if newClasses.isEmpty { return deleteAttribute("style") } return style(newClasses, condition) } - + /// toggles a single style value - func style(toggle value: String?, _ condition: Bool = true) -> Self { + public func style(toggle value: String?, _ condition: Bool = true) -> Self { guard let value = value else { return self } @@ -230,66 +241,65 @@ public extension Tag { return style(add: value, condition) } - // MARK: - other global attributes - + /// Specifies a shortcut key to activate/focus an element - func accesskey(_ value: Character) -> Self { + public func accesskey(_ value: Character) -> Self { attribute("accesskey", String(value)) } - + /// Specifies whether the content of an element is editable or not - func contenteditable(_ value: Bool) -> Self { + public func contenteditable(_ value: Bool) -> Self { attribute("contenteditable", String(value)) } - + /// Used to store custom data private to the page or application - func data(key: String, _ value: String) -> Self { + public func data(key: String, _ value: String) -> Self { attribute("data-" + key, value) } /// Specifies the text direction for the content in an element - func dir(_ value: TextDirection = .ltr) -> Self { + public func dir(_ value: TextDirection = .ltr) -> Self { attribute("dir", value.rawValue) } - + /// Specifies whether an element is draggable or not - func draggable(_ value: Draggable = .auto) -> Self { + public func draggable(_ value: Draggable = .auto) -> Self { attribute("draggable", value.rawValue) } - + /// Specifies that an element is not yet, or is no longer, relevant - func hidden(_ value: Bool? = nil) -> Self { + public func hidden(_ value: Bool? = nil) -> Self { attribute("hidden", value?.description) } - + /// Specifies a unique id for an element - func `id`(_ value: String) -> Self { + public func `id`(_ value: String) -> Self { attribute("id", value) } - + /// Specifies the language of the element's content - func lang(_ value: String) -> Self { + public func lang(_ value: String) -> Self { attribute("lang", value) } - + /// Specifies whether the element is to have its spelling and grammar checked or not - func spellcheck(_ value: Bool) -> Self { + public func spellcheck(_ value: Bool) -> Self { attribute("spellcheck", String(value)) } - + /// Specifies the tabbing order of an element - func tabindex(_ value: Int) -> Self { + public func tabindex(_ value: Int) -> Self { attribute("tabindex", String(value)) } - + /// Specifies extra information about an element - func title(_ value: String) -> Self { + public func title(_ value: String) -> Self { attribute("title", value) } - + /// Specifies whether the content of an element should be translated or not - func translate(_ value: Translate) -> Self { + public func translate(_ value: Translate) -> Self { attribute("translate", value.rawValue) } } diff --git a/Sources/SwiftHtml/Attributes/Target.swift b/Sources/SwiftHtml/Attributes/Target.swift index b02493d..add7119 100644 --- a/Sources/SwiftHtml/Attributes/Target.swift +++ b/Sources/SwiftHtml/Attributes/Target.swift @@ -27,7 +27,7 @@ public enum TargetFrame { case top /// Opens the linked document in the named iframe case frame(String) - + var rawValue: String { switch self { case .blank: diff --git a/Sources/SwiftHtml/Elements/A.swift b/Sources/SwiftHtml/Elements/A.swift index 0a342fd..83abf28 100644 --- a/Sources/SwiftHtml/Elements/A.swift +++ b/Sources/SwiftHtml/Elements/A.swift @@ -18,9 +18,9 @@ open class A: Tag { } -public extension A { - - enum Rel: String { +extension A { + + public enum Rel: String { /// Provides a link to an alternate representation of the document (i.e. print page, translated or mirror) case alternate /// Provides a link to the author of the document @@ -49,65 +49,65 @@ public extension A { /// A tag (keyword) for the current document case tag } - + /// Specifies that the target will be downloaded when a user clicks on the hyperlink - func download(_ value: String? = nil) -> Self { + public func download(_ value: String? = nil) -> Self { flagAttribute("download", value) } - + /// Specifies the URL of the page the link goes to - func href(_ value: String) -> Self { + public func href(_ value: String) -> Self { attribute("href", value) } - + /// Specifies the language of the linked document - func hreflang(_ value: String) -> Self { + public func hreflang(_ value: String) -> Self { attribute("hreflang", value) } - + /// Specifies what media/device the linked document is optimized for - func media(_ value: String) -> Self { + public func media(_ value: String) -> Self { attribute("media", value) } - + /// Specifies what media/device the linked document is optimized for /// /// If multiple queries were provided they're going to be concatenated with an `and` operand - func media(_ queries: MediaQuery...) -> Self { - return media(queries) + public func media(_ queries: MediaQuery...) -> Self { + media(queries) } - + /// Specifies what media/device the linked document is optimized for /// /// If multiple queries were provided they're going to be concatenated with an `and` operand - func media(_ queries: [MediaQuery]) -> Self { - return media(queries.map(\.value).joined(separator: " and ")) + public func media(_ queries: [MediaQuery]) -> Self { + media(queries.map(\.value).joined(separator: " and ")) } - + /// Specifies a space-separated list of URLs to which, when the link is followed, post requests with the body ping will be sent by the browser (in the background). /// /// Typically used for tracking. - func ping(_ value: [String]) -> Self { + public func ping(_ value: [String]) -> Self { attribute("ping", value.joined(separator: " ")) } - + /// Specifies which referrer information to send with the link - func refererPolicy(_ value: RefererPolicy = .origin) -> Self { + public func refererPolicy(_ value: RefererPolicy = .origin) -> Self { attribute("referrerpolicy", value.rawValue) } - + /// Specifies the relationship between the current document and the linked document - func rel(_ value: Rel) -> Self { + public func rel(_ value: Rel) -> Self { attribute("rel", value.rawValue) } /// Specifies where to open the linked document - func target(_ value: TargetFrame, _ condition: Bool = true) -> Self { + public func target(_ value: TargetFrame, _ condition: Bool = true) -> Self { attribute("target", value.rawValue, condition) } - + /// The type attribute specifies the Internet media type (formerly known as MIME type) of the linked document. - func type(_ value: String) -> Self { + public func type(_ value: String) -> Self { attribute("type", value) } } diff --git a/Sources/SwiftHtml/Elements/Abbr.swift b/Sources/SwiftHtml/Elements/Abbr.swift index bbc38fc..074e0a7 100644 --- a/Sources/SwiftHtml/Elements/Abbr.swift +++ b/Sources/SwiftHtml/Elements/Abbr.swift @@ -11,4 +11,3 @@ open class Abbr: Tag { } - diff --git a/Sources/SwiftHtml/Elements/Area.swift b/Sources/SwiftHtml/Elements/Area.swift index 0ce5ca8..1819151 100644 --- a/Sources/SwiftHtml/Elements/Area.swift +++ b/Sources/SwiftHtml/Elements/Area.swift @@ -6,12 +6,12 @@ // /// The tag defines an area inside an image map (an image map is an image with clickable areas). -/// +/// /// elements are always nested inside a tag. -/// +/// /// **Note:** The usemap attribute in is associated with the element's name attribute, and creates a relationship between the image and the map. open class Area: EmptyTag { - + public enum Rel: String { /// Links to an alternate version of the document (i.e. print page, translated or mirror) case alternate @@ -39,7 +39,7 @@ open class Area: EmptyTag { /// A tag (keyword) for the current document case tag } - + public enum Shape: String { /// Specifies the entire region case `default` @@ -53,73 +53,73 @@ open class Area: EmptyTag { } -public extension Area { +extension Area { /// Specifies an alternate text for the area. Required if the href attribute is present - func alt(_ value: String) -> Self { + public func alt(_ value: String) -> Self { attribute("alt", value) } - + /// Specifies the coordinates of the area - func coords(_ values: Double...) -> Self { - attribute("coords", values.map {String($0) }.joined(separator: ",")) + public func coords(_ values: Double...) -> Self { + attribute("coords", values.map { String($0) }.joined(separator: ",")) } - + /// Specifies that the target will be downloaded when a user clicks on the hyperlink - func download(_ value: String) -> Self { + public func download(_ value: String) -> Self { attribute("download", value) } - + /// Specifies the hyperlink target for the area - func href(_ value: String) -> Self { + public func href(_ value: String) -> Self { attribute("href", value) } - + /// Specifies the language of the target URL - func hreflang(_ value: String) -> Self { + public func hreflang(_ value: String) -> Self { attribute("hreflang", value) } - + /// Specifies what media/device the target URL is optimized for - func media(_ value: String) -> Self { + public func media(_ value: String) -> Self { attribute("media", value) } - + /// Specifies what media/device the linked document is optimized for /// /// If multiple queries were provided they're going to be concatenated with an `and` operand - func media(_ queries: MediaQuery...) -> Self { - return media(queries) + public func media(_ queries: MediaQuery...) -> Self { + media(queries) } - + /// Specifies what media/device the linked document is optimized for /// /// If multiple queries were provided they're going to be concatenated with an `and` operand - func media(_ queries: [MediaQuery]) -> Self { - return media(queries.map(\.value).joined(separator: " and ")) + public func media(_ queries: [MediaQuery]) -> Self { + media(queries.map(\.value).joined(separator: " and ")) } - + /// Specifies which referrer information to send with the link - func refererPolicy(_ value: RefererPolicy = .origin) -> Self { + public func refererPolicy(_ value: RefererPolicy = .origin) -> Self { attribute("referrerpolicy", value.rawValue) } - + /// Specifies the relationship between the current document and the target URL - func rel(_ value: Rel) -> Self { + public func rel(_ value: Rel) -> Self { attribute("rel", value.rawValue) } - + /// Specifies the shape of the area - func shape(_ value: Shape) -> Self { + public func shape(_ value: Shape) -> Self { attribute("shape", value.rawValue) } - + /// Specifies where to open the target URL - func target(_ value: TargetFrame) -> Self { + public func target(_ value: TargetFrame) -> Self { attribute("target", value.rawValue) } - + /// Specifies the media type of the target URL - func type(_ value: String) -> Self { + public func type(_ value: String) -> Self { attribute("type", value) } } diff --git a/Sources/SwiftHtml/Elements/Article.swift b/Sources/SwiftHtml/Elements/Article.swift index b78d593..c860f76 100644 --- a/Sources/SwiftHtml/Elements/Article.swift +++ b/Sources/SwiftHtml/Elements/Article.swift @@ -5,9 +5,9 @@ // Created by Tibor Bodecs on 2021. 07. 19.. // -public extension Node { +extension Node { - static func article() -> Node { + public static func article() -> Node { Node(type: .standard, name: "article") } } @@ -27,4 +27,3 @@ public extension Node { open class Article: Tag { } - diff --git a/Sources/SwiftHtml/Elements/Aside.swift b/Sources/SwiftHtml/Elements/Aside.swift index 93b1ab9..de12f31 100644 --- a/Sources/SwiftHtml/Elements/Aside.swift +++ b/Sources/SwiftHtml/Elements/Aside.swift @@ -6,14 +6,13 @@ // /// The `