diff --git a/Sources/HummingbirdMustache/Library+FileSystem.swift b/Sources/HummingbirdMustache/Library+FileSystem.swift index bab2fc0..11fb28f 100644 --- a/Sources/HummingbirdMustache/Library+FileSystem.swift +++ b/Sources/HummingbirdMustache/Library+FileSystem.swift @@ -1,6 +1,7 @@ import Foundation extension HBMustacheLibrary { + /// Load templates from a folder func loadTemplates(from directory: String, withExtension extension: String = "mustache") { var directory = directory if !directory.hasSuffix("/") { diff --git a/Sources/HummingbirdMustache/Library.swift b/Sources/HummingbirdMustache/Library.swift index c97e232..69b115b 100644 --- a/Sources/HummingbirdMustache/Library.swift +++ b/Sources/HummingbirdMustache/Library.swift @@ -1,23 +1,47 @@ - +/// Class holding a collection of mustache templates. +/// +/// Each template can reference the others via a partial using the name the template is registered under +/// ``` +/// {{#sequence}}{{>entry}}{{/sequence}} +/// ``` public class HBMustacheLibrary { + /// Initialize empty library public init() { self.templates = [:] } - - public init(directory: String) { + + /// Initialize library with contents of folder. + /// + /// Each template is registered with the name of the file minus its extension. The search through + /// the folder is recursive and templates in subfolders will be registered with the name `subfolder/template`. + /// - Parameter directory: Directory to look for mustache templates + /// - Parameter extension: Extension of files to look for + public init(directory: String, withExtension extension: String = "mustache") { self.templates = [:] self.loadTemplates(from: directory) } - + + /// Register template under name + /// - Parameters: + /// - template: Template + /// - name: Name of template public func register(_ template: HBMustacheTemplate, named name: String) { template.setLibrary(self) templates[name] = template } - + + /// Return template registed with name + /// - Parameter name: name to search for + /// - Returns: Template public func getTemplate(named name: String) -> HBMustacheTemplate? { templates[name] } - + + /// Render object using templated with name + /// - Parameters: + /// - object: Object to render + /// - name: Name of template + /// - Returns: Rendered text public func render(_ object: Any, withTemplateNamed name: String) -> String? { guard let template = templates[name] else { return nil } return template.render(object) diff --git a/Sources/HummingbirdMustache/Mirror.swift b/Sources/HummingbirdMustache/Mirror.swift index 6ade425..e169942 100644 --- a/Sources/HummingbirdMustache/Mirror.swift +++ b/Sources/HummingbirdMustache/Mirror.swift @@ -1,13 +1,7 @@ -func unwrapOptional(_ object: Any) -> Any? { - let mirror = Mirror(reflecting: object) - guard mirror.displayStyle == .optional else { return object } - guard let first = mirror.children.first else { return nil } - return first.value -} - extension Mirror { + /// Return value from Mirror given name func getValue(forKey key: String) -> Any? { guard let matched = children.filter({ $0.label == key }).first else { return nil @@ -15,3 +9,12 @@ extension Mirror { return unwrapOptional(matched.value) } } + +/// Return object and if it is an Optional return object Optional holds +private func unwrapOptional(_ object: Any) -> Any? { + let mirror = Mirror(reflecting: object) + guard mirror.displayStyle == .optional else { return object } + guard let first = mirror.children.first else { return nil } + return first.value +} + diff --git a/Sources/HummingbirdMustache/Parent.swift b/Sources/HummingbirdMustache/Parent.swift index a315e59..85206f4 100644 --- a/Sources/HummingbirdMustache/Parent.swift +++ b/Sources/HummingbirdMustache/Parent.swift @@ -1,13 +1,12 @@ +/// Protocol for object that has a custom method for accessing their children, instead +/// of using Mirror protocol HBMustacheParent { func child(named: String) -> Any? } -extension HBMustacheParent { - // default child to nil - func child(named: String) -> Any? { return nil } -} - +/// Extend dictionary where the key is a string so that it uses the key values to access +/// it values extension Dictionary: HBMustacheParent where Key == String { func child(named: String) -> Any? { return self[named] } } diff --git a/Sources/HummingbirdMustache/Parser.swift b/Sources/HummingbirdMustache/Parser.swift index 6deeb3e..8abd359 100644 --- a/Sources/HummingbirdMustache/Parser.swift +++ b/Sources/HummingbirdMustache/Parser.swift @@ -3,7 +3,7 @@ // Half inspired by Reader class from John Sundell's Ink project // https://github.com/JohnSundell/Ink/blob/master/Sources/Ink/Internal/Reader.swift // with optimisation working ie removing String and doing my own UTF8 processing inspired by Fabian Fett's work in -// https://github.com/fabianfett/pure-swift-json/blob/master/Sources/PureSwiftJSONParsing/DocumentReader.swift +// https://github.com/swift-extras/swift-extras-json/blob/main/Sources/ExtrasJSON/Parsing/DocumentReader.swift // // This is a copy of the parser from Hummingbird. I am not using the version in Hummingbird to avoid the dependency import Foundation diff --git a/Sources/HummingbirdMustache/Sequence.swift b/Sources/HummingbirdMustache/Sequence.swift index 2edb4dd..4ef37be 100644 --- a/Sources/HummingbirdMustache/Sequence.swift +++ b/Sources/HummingbirdMustache/Sequence.swift @@ -1,11 +1,15 @@ -protocol HBMustacheSequence { +/// Protocol for objects that can be rendered as a sequence in Mustache +public protocol HBMustacheSequence { + /// Render section using template func renderSection(with template: HBMustacheTemplate) -> String + /// Render inverted section using template func renderInvertedSection(with template: HBMustacheTemplate) -> String } extension Sequence { - func renderSection(with template: HBMustacheTemplate) -> String { + /// Render section using template + public func renderSection(with template: HBMustacheTemplate) -> String { var string = "" var context = HBMustacheContext(first: true) var iterator = self.makeIterator() @@ -24,7 +28,8 @@ extension Sequence { return string } - func renderInvertedSection(with template: HBMustacheTemplate) -> String { + /// Render inverted section using template + public func renderInvertedSection(with template: HBMustacheTemplate) -> String { var iterator = makeIterator() if iterator.next() == nil { return template.render(self) @@ -35,4 +40,5 @@ extension Sequence { } extension Array: HBMustacheSequence {} +extension Set: HBMustacheSequence {} extension ReversedCollection: HBMustacheSequence {} diff --git a/Sources/HummingbirdMustache/Template+Parser.swift b/Sources/HummingbirdMustache/Template+Parser.swift index c97a73a..191fce9 100644 --- a/Sources/HummingbirdMustache/Template+Parser.swift +++ b/Sources/HummingbirdMustache/Template+Parser.swift @@ -6,11 +6,13 @@ extension HBMustacheTemplate { case expectedSectionEnd } + /// parse mustache text to generate a list of tokens static func parse(_ string: String) throws -> [Token] { var parser = HBParser(string) return try parse(&parser, sectionName: nil) } + /// parse section in mustache text static func parse(_ parser: inout HBParser, sectionName: String?) throws -> [Token] { var tokens: [Token] = [] while !parser.reachedEnd() { @@ -78,6 +80,7 @@ extension HBMustacheTemplate { return tokens } + /// parse variable name static func parseName(_ parser: inout HBParser) throws -> (String, String?) { parser.read(while: \.isWhitespace) var text = parser.read(while: sectionNameChars ) @@ -88,6 +91,7 @@ extension HBMustacheTemplate { if text.reachedEnd() { return (text.string, nil) } else { + // parse function parameter, as we have just parsed a function name guard text.current() == "(" else { throw Error.unfinishedName } text.unsafeAdvance() let string2 = text.read(while: sectionNameCharsWithoutBrackets) diff --git a/Sources/HummingbirdMustache/Template.swift b/Sources/HummingbirdMustache/Template.swift index 3c8cf1e..a85d48c 100644 --- a/Sources/HummingbirdMustache/Template.swift +++ b/Sources/HummingbirdMustache/Template.swift @@ -1,17 +1,23 @@ - +/// Class holding Mustache template public class HBMustacheTemplate { + /// Initialize template + /// - Parameter string: Template text + /// - Throws: HBMustacheTemplate.Error public init(string: String) throws { self.tokens = try Self.parse(string) } - internal init(_ tokens: [Token]) { - self.tokens = tokens - } - + /// Render object using this template + /// - Parameter object: Object to render + /// - Returns: Rendered text public func render(_ object: Any) -> String { self.render(object, context: nil) } + internal init(_ tokens: [Token]) { + self.tokens = tokens + } + internal func setLibrary(_ library: HBMustacheLibrary) { self.library = library for token in tokens {