100 lines
3.1 KiB
Swift
100 lines
3.1 KiB
Swift
import Foundation
|
|
import Saga
|
|
|
|
extension Date {
|
|
func formatted(_ format: String) -> String {
|
|
let formatter = DateFormatter()
|
|
formatter.dateFormat = format
|
|
return formatter.string(from: self)
|
|
}
|
|
}
|
|
|
|
extension Item where M == ArticleMetadata {
|
|
|
|
/// The article summary, which is used when displaying a list of articles.
|
|
var summary: String {
|
|
// Use the summary if supplied in the articles front-matter.
|
|
if let summary = metadata.summary {
|
|
return summary
|
|
}
|
|
// Generate the summary from the first 255 words of the article.
|
|
return String(body.withoutHtmlTags.truncate())
|
|
}
|
|
|
|
/// The articles banner image path.
|
|
var imagePath: String {
|
|
let image = metadata.image ?? "\(filenameWithoutExtension).png"
|
|
return "/articles/images/\(image)"
|
|
}
|
|
|
|
/// An easy way to only get public articles, since ArticleMetadata.public is optional
|
|
var `public`: Bool {
|
|
#if DEBUG
|
|
return true
|
|
#else
|
|
return metadata.public ?? true
|
|
#endif
|
|
}
|
|
|
|
func getPrimaryTag() -> String? {
|
|
guard let primaryTag = metadata.primaryTag else {
|
|
guard metadata.tags.count == 1 else { return nil }
|
|
return metadata.tags[0]
|
|
}
|
|
return primaryTag
|
|
}
|
|
|
|
func getDate() -> Date {
|
|
guard let date = metadata.date else { return date }
|
|
return date
|
|
}
|
|
|
|
func getUpdatedDate() -> Date? {
|
|
return metadata.updated
|
|
}
|
|
}
|
|
|
|
// NOTE: Most of these are taken from https://github.com/loopwerk/loopwerk.io
|
|
|
|
extension String {
|
|
|
|
/// Used to generate the word count of an article, to be displayed as metadata about
|
|
/// the article.
|
|
var numberOfWords: Int {
|
|
let characterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters)
|
|
let components = self.components(separatedBy: characterSet)
|
|
return components.filter { !$0.isEmpty }.count
|
|
}
|
|
|
|
// This is a sloppy implementation but sadly `NSAttributedString(data:options:documentAttributes:)`
|
|
// is not available in CoreFoundation, and as such can't run on Linux (blocking CI builds).
|
|
var withoutHtmlTags: String {
|
|
return replacingOccurrences(of: "(?m)<pre><span></span><code>[\\s\\S]+?</code></pre>", with: "", options: .regularExpression, range: nil)
|
|
.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil)
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
}
|
|
|
|
/// See https://jinja2docs.readthedocs.io/en/stable/templates.html#truncate
|
|
func truncate(length: Int = 255, killWords: Bool = false, end: String = "...", leeway: Int = 5) -> String {
|
|
if count <= length + leeway {
|
|
return self
|
|
}
|
|
|
|
if killWords {
|
|
return prefix(length - end.count) + end
|
|
}
|
|
|
|
return prefix(length - end.count).split(separator: " ").dropLast().joined(separator: " ") + end
|
|
}
|
|
|
|
/// Removes unwanted breaks that are caused by the way markdown files are formatted by
|
|
/// prettier in my neovim setup. When not applied then paragraphs get split up improperly
|
|
/// causing them to display funny on the site.
|
|
var removeBreaks: String {
|
|
replacingOccurrences(of: "<br>", with: "")
|
|
.replacingOccurrences(of: "<br />", with: "")
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
}
|
|
|
|
}
|