Added (first version of) sample Hummingbird app. (#4)

This PR contains the work done to:
* Implemented a basic `Hummingbird` application in which to integrate the `HummingbirdDocC` library.
* Added the *ArgumentParser* package dependency to the `Package.swift` file;
* Added a new *sample* target to the `Package.swift` file;
* Added library and documentation tasks to the `Makefile` file.

Reviewed-on: #4
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit was merged in pull request #4.
This commit is contained in:
2025-09-30 15:38:12 +00:00
committed by Javier Cicchelli
parent 3a9e3d176f
commit 1382f33ae6
49 changed files with 1095 additions and 488 deletions
@@ -0,0 +1,50 @@
// ===----------------------------------------------------------------------===
//
// This source file is part of the Hummingbird DocC Middleware open source project
//
// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors
// Licensed under the EUPL 1.2 or later.
//
// See LICENSE for license information
// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors
//
// ===----------------------------------------------------------------------===
import protocol Hummingbird.RequestContext
import struct Hummingbird.HTTPResponse
import struct Hummingbird.Request
import struct Logging.Logger
extension Logger.Metadata {
// MARK: Functions
/// Generates a dictionary to use as metadata for events to log into the logging system.
/// - Parameters:
/// - context: A type that contains all the parameters associated with a given request, and that conforms to the `RequestContext` protocol.
/// - request: A type that contains all the parameters to process as a request.
/// - statusCode: A representation of a response status to provide as a response.
/// - redirect: A URI path to use in a redirection event, if any.
/// - Returns: A generated metadata dictionary for an event to log into the logging system.
static func metadata(
context: any RequestContext,
request: Request,
statusCode: HTTPResponse.Status,
redirect: String? = nil
) -> Self {
var metadata: Logger.Metadata = [
"hb.request.id": "\(context.id)",
"hb.request.method": "\(request.method.rawValue)",
"hb.request.path": "\(request.uri.path)",
"hb.request.status": "\(statusCode.code)"
]
if let redirect {
metadata["hb.request.redirect"] = "\(redirect)"
}
return metadata
}
}
@@ -0,0 +1,30 @@
// ===----------------------------------------------------------------------===
//
// This source file is part of the Hummingbird DocC Middleware open source project
//
// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors
// Licensed under the EUPL 1.2 or later.
//
// See LICENSE for license information
// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors
//
// ===----------------------------------------------------------------------===
extension String {
/// An empty string.
static let empty = ""
/// A namespace that defines logging values.
enum Logging {
/// A name of the middleware that triggered a logging event.
static let source = "DocCMiddleware"
}
/// A namespace that defines relative path values.
enum Path {
/// A forwarding slash.
static let forwardSlash = "/"
/// An indication of a previous folder in a path component.
static let previousFolder = ".."
}
}
@@ -0,0 +1,38 @@
// ===----------------------------------------------------------------------===
//
// This source file is part of the Hummingbird DocC Middleware open source project
//
// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors
// Licensed under the EUPL 1.2 or later.
//
// See LICENSE for license information
// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors
//
// ===----------------------------------------------------------------------===
extension String {
/// A namespace that defines the format patterns used to generate strings.
enum Format {
/// A namespace that defines the format patterns used to generate relative path representations.
enum Path {
/// A format pattern used to generate relative paths that starts with the `/` string and finishes with the `.doccarchive` string.
static let archive = "/%@.doccarchive"
/// A format pattern used to generate relative paths that starts with the `/data` string.
static let data = "/data/%@"
/// A format pattern used to generate relative paths that starts with the `/docs` string.
static let docs = "/docs/%@"
/// A format pattern used to generate relative paths that finishes with the `/documentation` string.
static let documentation = "%@documentation"
/// A format pattern used to generate relative paths for JSON documentation files.
static let documentationJSON = "/data/documentation/%@.json"
/// A format pattern used to generate relative paths that starts and finishes with the `/` string.
static let folder = "/%@/"
///A format pattern used to generate relative paths that finishes with the `/` string.
static let forwardSlash = "%@/"
/// A format pattern used to generate relative paths for index files.
static let index = "%@/%@/index.html"
/// A format pattern used to generate relative paths that starts with the `/` string.
static let root = "/%@"
}
}
}
@@ -0,0 +1,46 @@
// ===----------------------------------------------------------------------===
//
// This source file is part of the Hummingbird DocC Middleware open source project
//
// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors
// Licensed under the EUPL 1.2 or later.
//
// See LICENSE for license information
// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors
//
// ===----------------------------------------------------------------------===
import RegexBuilder
extension String {
/// Subtracts a prefix from a string.
/// - Parameters:
/// - prefix: A prefix to remove from a string.
/// - Returns: A new string with a prefix removed, if any.
func subtract(
_ prefix: String
) -> String? {
let reference = Reference(String.self)
let pattern = Regex {
prefix
Optionally {
Capture(as: reference) {
OneOrMore(.anyNonNewline)
} transform: { output in
String(output)
}
}
}
guard let matches = self.prefixMatch(of: pattern) else {
return nil
}
guard let subtracted = matches.output.1 else {
return .empty
}
return subtracted
}
}