Skip to content

Instantly share code, notes, and snippets.

@jaredsinclair
Created April 8, 2019 01:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredsinclair/7ea54d4e3e75e6394f72a53b5ed548df to your computer and use it in GitHub Desktop.
Save jaredsinclair/7ea54d4e3e75e6394f72a53b5ed548df to your computer and use it in GitHub Desktop.
Transform a Feedbin JSON backup file into a directory of Jekyll-formatted posts
import Foundation
/*
Alls you gotta do is (in Main.swift):
1. Initialize an Importer with the file URL to the JSON file (origin) and a URL to the directory that will contain all the Jekyll-formatted posts (destination).
2. Call the `run()` method on the Importer.
*/
struct Post: Codable {
static let filenameFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
return formatter
}()
static let frontmatterFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd HH:mm:ss"
return formatter
}()
static let disallowedCharacters = NSCharacterSet
.alphanumerics
.union(CharacterSet(charactersIn: "-"))
.inverted
let title: String
let url: String
let content: String
let published: Date
var fileName: String {
let date = Post.filenameFormatter.string(from: published)
let slug = title
.lowercased()
.replacingOccurrences(of: " ", with: "-")
.components(separatedBy: Post.disallowedCharacters)
.joined(separator: "")
.prefix(32)
.trimmingCharacters(in: CharacterSet(charactersIn: "-"))
return "\(date)-\(slug).html"
}
var fileContent: String {
let dateStamp = Post.frontmatterFormatter.string(from: published)
return """
---
layout: post
title: "\(title)"
date: \(dateStamp)
categories:
navtab: blog
---
\(content)
"""
}
}
public final class Importer {
let origin: URL
let destination: URL
let formatter: ISO8601DateFormatter
public init(origin: URL, destination: URL) {
print("Will read data from \(origin)")
self.origin = origin
self.destination = destination
self.formatter = ISO8601DateFormatter()
// init satisfied
formatter.formatOptions = formatter.formatOptions.union(.withFractionalSeconds)
}
public func run() throws {
let data = try Data(contentsOf: origin)
try decode(data)
}
private func decode(_ data: Data) throws {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(formatter.parseDate)
let posts = try decoder.decode([Post].self, from: data)
posts.forEach { post in
let fileUrl = destination.appendingPathComponent(post.fileName, isDirectory: false)
let fileContent = post.fileContent.data(using: .utf8)!
try! fileContent.write(to: fileUrl, options: .atomicWrite)
}
}
}
extension ISO8601DateFormatter {
enum DateError: Error {
case invalidDateFormat
}
func parseDate(using decoder: Decoder) throws -> Date {
let string = try decoder.singleValueContainer().decode(String.self)
guard let date = date(from: string) else {
throw DateError.invalidDateFormat
}
return date
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment