CHGenerator/WebsiteGenerator/Generic/GenericMetadata.swift
Christoph Hagen 2e6542225f Improve error printing
Improve path printing

Update comment
2022-08-25 00:11:24 +02:00

175 lines
5.0 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"
/**
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 url where the site will be deployed.
This value is required to build absolute links for link previews.
- Note: Only the root level value is used.
- Note: The path does not need to contain a trailing slash.
*/
let deployedBaseUrl: 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: ThumbnailStyle?
/**
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?
/**
The localized metadata for each language.
*/
let languages: [LocalizedMetadata]?
}
extension GenericMetadata: Codable {
private static var knownKeyList: [CodingKeys] {
[
.author,
.topBarTitle,
.deployedBaseUrl,
.date,
.endDate,
.state,
.sortIndex,
.externalFiles,
.requiredFiles,
.thumbnailStyle,
.useManualSorting,
.overviewItemCount,
.languages
]
}
static var knownKeys: Set<String> {
Set(knownKeyList.map { $0.stringValue })
}
}
extension GenericMetadata {
/**
Decode metadata in a folder.
- Parameter source: The path to the metadata file, relative to the source root
- Parameter context: The context for the element (validation, file access, etc.)
- Note: The decoding routine also checks for unknown properties, and writes them to the output.
*/
init?(source: String, with context: Context) throws {
guard let data = try context.fileSystem.loadDataContent(inputPath: 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) {
context.validation.unknown(property: key.stringValue, source: source)
}
return key
}
// Two levels means we're decoding the localized metadata
if !knownLocalizedKeys.contains(key.stringValue) {
context.validation.unknown(property: key.stringValue, source: source)
}
return key
}
do {
self = try decoder.decode(from: data)
} catch {
context.validation.failedToOpen(GenericMetadata.metadataFileName, requiredBy: source, error: error)
return nil
}
}
}