CHGenerator/WebsiteGenerator/Content/GenericMetadata.swift
2022-08-30 20:09:12 +02:00

207 lines
5.8 KiB
Swift

import Foundation
/**
The metadata for all site elements.
*/
struct GenericMetadata {
/**
The name of the metadata file contained in the folder of each site element.
*/
static let metadataFileName = "metadata.json"
/**
A custom id to uniquely identify the element on the site.
The id is used for short-hand links to pages, in the form of `![page](page_id)`
for thumbnail previews or `[text](page:page_id)` for simple links.
If no custom id is set, then the name of the element folder is used.
*/
let customId: String?
/**
The author of the content.
If no author is set, then the author from the parent element is used.
*/
let author: String?
/**
The title used in the top bar of the website, next to the logo.
This title can be HTML content, and only the root level value is used.
*/
let topBarTitle: String?
/**
The (start) date of the element.
The date is printed on content pages and may also used for sorting elements,
depending on the `useManualSorting` property of the parent.
*/
let date: String?
/**
The end date of the element.
This property can be used to specify a date range for a content page.
*/
let endDate: String?
/**
The deployment state of the page.
- Note: This property defaults to ``PageState.standard`
*/
let state: String?
/**
The sort index of the page for manual sorting.
- Note: This property is only used (and must be set) if `useManualSorting` option of the parent is set.
*/
let sortIndex: Int?
/**
All files which may occur in content but is stored externally.
Missing files which would otherwise produce a warning are ignored when included here.
- Note: This property defaults to an empty set.
*/
let externalFiles: Set<String>?
/**
Specifies additional files which should be copied to the destination when generating the content.
- Note: This property defaults to an empty set.
*/
let requiredFiles: Set<String>?
/**
The style of thumbnail to use when generating overviews.
- Note: This property is only relevant for sections.
- Note: This property is inherited from the parent if not specified.
*/
let thumbnailStyle: String?
/**
Sort the child elements by their `sortIndex` property when generating overviews, instead of using the `date`.
- Note: This property is only relevant for sections.
- Note: This property defaults to `false`
*/
let useManualSorting: Bool?
/**
The number of items to show when generating overviews of this element.
- Note: This property is only relevant for sections.
- Note: This property is inherited from the parent if not specified.
*/
let overviewItemCount: Int?
/**
Indicate that no header should be generated automatically.
This option assumes that custom header code is present in the page source files
- Note: If not specified, this property defaults to `false`.
*/
let useCustomHeader: Bool?
/**
The localized metadata for each language.
*/
let languages: [LocalizedMetadata]?
}
extension GenericMetadata: Codable {
private static var knownKeyList: [CodingKeys] {
[
.customId,
.author,
.topBarTitle,
.date,
.endDate,
.state,
.sortIndex,
.externalFiles,
.requiredFiles,
.thumbnailStyle,
.useManualSorting,
.overviewItemCount,
.useCustomHeader,
.languages,
]
}
static var knownKeys: Set<String> {
Set(knownKeyList.map { $0.stringValue })
}
}
extension GenericMetadata {
/**
Decode metadata in a folder.
- Parameter data: The binary data of the metadata file.
- Parameter source: The path to the metadata file, relative to the source root
- Note: The decoding routine also checks for unknown properties, and writes them to the output.
- Note: Uses global objects
*/
init?(source: String) {
guard let data = files.dataOfOptionalFile(atPath: source, source: source) else {
return nil
}
let decoder = JSONDecoder()
let knownKeys = GenericMetadata.knownKeys
let knownLocalizedKeys = LocalizedMetadata.knownKeys
decoder.keyDecodingStrategy = .custom { keys in
let key = keys.last!
// Only one key means we are decoding the generic metadata
guard keys.count > 1 else {
if !knownKeys.contains(key.stringValue) {
log.unknown(property: key.stringValue, source: source)
}
return key
}
// Two levels means we're decoding the localized metadata
if !knownLocalizedKeys.contains(key.stringValue) {
log.unknown(property: key.stringValue, source: source)
}
return key
}
do {
self = try decoder.decode(from: data)
} catch {
print("Here \(data)")
log.failedToOpen(GenericMetadata.metadataFileName, requiredBy: source, error: error)
return nil
}
}
}
extension GenericMetadata {
static var full: GenericMetadata {
.init(
customId: "",
author: "",
topBarTitle: "",
date: "",
endDate: "",
state: "",
sortIndex: 1,
externalFiles: [],
requiredFiles: [],
thumbnailStyle: "",
useManualSorting: false,
overviewItemCount: 6,
useCustomHeader: false,
languages: [.full])
}
}