import Foundation extension Content { func saveToDisk() throws { guard storage.contentScope != nil else { print("Storage not initialized, not saving content") return } var failedSaves = 0 failedSaves += pages.count { !storage.save(pageMetadata: $0.pageFile, for: $0.id) } failedSaves += posts.count { !storage.save(post: $0.postFile, for: $0.id) } failedSaves += tags.count { !storage.save(tagMetadata: $0.tagFile, for: $0.id) } failedSaves.increment(!storage.save(settings: settings.file)) let fileDescriptions: [FileDescriptions] = files.sorted().compactMap { file in guard !file.english.isEmpty || !file.german.isEmpty else { return nil } return FileDescriptions( fileId: file.id, german: file.german.nonEmpty, english: file.english.nonEmpty) } failedSaves.increment(!storage.save(fileDescriptions: fileDescriptions)) failedSaves.increment(!storage.save(tagOverview: tagOverview?.file)) let externalFileList = files.filter { $0.isExternallyStored }.map { $0.id } failedSaves.increment(!storage.save(externalFileList: externalFileList)) if failedSaves > 0 { print("Save partially failed with \(failedSaves) errors") } } func removeUnlinkedFiles() -> Bool { var success = true if !storage.deletePostFiles(notIn: posts.map { $0.id }) { success = false } if !storage.deletePageFiles(notIn: pages.map { $0.id }) { success = false } if !storage.deleteTagFiles(notIn: tags.map { $0.id }) { success = false } if !storage.deleteFileResources(notIn: files.map { $0.id }) { success = false } return success } } private extension Page { var pageFile: PageFile { .init(isDraft: isDraft, externalLink: externalLink, tags: tags.map { $0.id }, createdDate: createdDate, startDate: startDate, endDate: hasEndDate ? endDate : nil, german: german.pageFile, english: english.pageFile, requiredFiles: requiredFiles.nonEmpty?.map { $0.id }.sorted()) } } private extension LocalizedPage { var pageFile: LocalizedPageFile { .init(url: urlString, title: title, linkPreviewImage: linkPreviewImage?.id, linkPreviewTitle: linkPreviewTitle, linkPreviewDescription: linkPreviewDescription, lastModifiedDate: lastModified, originalURL: originalUrl) } } private extension Post { var postFile: PostFile { .init( isDraft: isDraft, createdDate: createdDate, startDate: startDate, endDate: hasEndDate ? endDate : nil, tags: tags.map { $0.id }, german: german.postFile, english: english.postFile, linkedPageId: linkedPage?.id) } } private extension LocalizedPost { var postFile: LocalizedPostFile { .init(images: images.map { $0.id }, title: title.nonEmpty, content: text, lastModifiedDate: lastModified, linkPreviewImage: linkPreviewImage?.id, linkPreviewTitle: linkPreviewTitle, linkPreviewDescription: linkPreviewDescription) } } private extension Tag { var tagFile: TagFile { .init(id: id, isVisible: isVisible, german: german.tagFile, english: english.tagFile) } } private extension LocalizedTag { var tagFile: LocalizedTagFile { .init(urlComponent: urlComponent, name: name, subtitle: subtitle, description: description, thumbnail: linkPreviewImage?.id, originalURL: originalUrl) } } extension Settings { var file: SettingsFile { .init( paths: paths.file, navigationItems: navigationItems.map { $0.itemType.id }, posts: posts.file, pages: pages.file, german: german.file, english: english.file) } } private extension PostSettings { var file: PostSettingsFile { .init(postsPerPage: postsPerPage, contentWidth: contentWidth, swiperCssFile: swiperCssFile?.id, swiperJsFile: swiperJsFile?.id, defaultCssFile: defaultCssFile?.id) } } private extension LocalizedPostSettings { var file: LocalizedPostSettingsFile { .init( feedTitle: title, feedDescription: description, feedUrlPrefix: feedUrlPrefix) } }