Skip to content

Instantly share code, notes, and snippets.

@seivan
Created December 26, 2016 13:21
Show Gist options
  • Select an option

  • Save seivan/9edbb32ef39f3340b24e500a617ed914 to your computer and use it in GitHub Desktop.

Select an option

Save seivan/9edbb32ef39f3340b24e500a617ed914 to your computer and use it in GitHub Desktop.
//
// EitherProtocol+Error.swift
// CaoCaoKit
//
// Created by Seivan Heidari on 16/08/29.
//
//
import Foundation
extension EitherProtocol where Self.Left == Error {
public var isError:Bool { return self.isLeft }
public var isValid:Bool { return self.isRight }
public var error:Error? { return self.value.left }
}
extension EitherProtocol where Self.Left == Error {
public var description: String { return self.either(transformRight: { ".OK(\($0))" }, orLeft: { ".ERROR(\($0))" } ) }
public var debugDescription: String { return self.description }
public func unwrapError() throws -> Right {
switch self.toConcreteType {
case let .right(r): return r
case let .left(l): throw l
}
}
public func flatMap<NextRight>(_ transform: (Right) throws -> Either<NextRight, Error>) -> Either<NextRight, Error> {
return self.either(transformRight: {
do { return try transform($0) }
catch { return Either.left(error) }
}, orLeft:Either.left)
}
public func filter(_ left:((Right) -> Error), transform: (Right) throws -> Bool) -> Either<Right, Error> {
switch self.toConcreteType {
case let .right(value): do { return try transform(value) ? Either.right(value) : Either.left(left(value)) } catch { return Either.left(left(value)) }
case let .left(value): return Either.left(value)
}
}
public func filter<NextLeft:Error>(_ left:NextLeft, transform: (Right) throws -> Bool) -> Either<Right, NextLeft> {
switch self.toConcreteType {
case let .right(value): do { return try transform(value) ? Either.right(value) : Either.left(left) } catch { return Either.left(left) }
case .left(_): return Either.left(left)
}
}
public func map<NextRight>(_ transform: (Right) throws -> NextRight) -> Either<NextRight, Left> {
return self.flatMap {
do {return try .right(transform($0)) }
catch { return .left(error) }
}
}
// public func apply<NextRight>(_ transform: ((Right) throws -> NextRight)) -> Either<NextRight, Left> {
// return Either.right(transform).flatMap(self.map)
// }
// public func apply<NextRight>(_ transform: Either<((Right) throws -> NextRight), Left>) -> Either<NextRight, Left> {
// return transform.flatMap(self.map)
// }
public func flatMapLeft(_ transform: (Left) throws -> Either<Right, Error>) -> Either<Right, Error> {
switch self.toConcreteType {
case let .right(value): return .right(value)
case let .left(value):
do { return try transform(value) }
catch { return .left(error) }
}
}
public func mapLeft(_ transform: (Left) throws -> Error) -> Either<Right, Error> {
return self.flatMapLeft {
do { return try .left(transform($0)) }
catch { return .left(error) }
}
}
}
extension LazyEitherProtocol where Self.Element : EitherProtocol, Self.Right == Self.Element.Right, Self.Left == Self.Element.Left, Self.Element.Left == Error {
public func flatMap<NextRight>(_ transform: @escaping (Right) throws -> Either<NextRight, Left>) -> LazyEither<Either<NextRight, Left>> {
return LazyEither(self.next().flatMap(transform))
}
public func filter(_ left: @escaping (Right) -> Left, transform: @escaping (Right) throws -> Bool) -> LazyEither<Either<Right, Left>> {
return LazyEither(self.next().filter(left, transform: transform))
}
public func filter<NextLeft:Error>(_ left:NextLeft, transform: @escaping (Right) throws -> Bool) -> LazyEither<Either<Right, NextLeft>> {
return LazyEither(self.next().filter(left, transform: transform))
}
public func map<NextRight>(_ transform: @escaping (Right) throws -> NextRight) -> LazyEither<Either<NextRight, Left>> {
return LazyEither(self.next().map(transform))
}
// public func apply<NextRight>(_ transform: @escaping ((Right) throws -> NextRight)) -> LazyEither<Either<NextRight, Left>> {
// return LazyEither(self.next().apply(transform))
// }
// public func apply<NextRight>(_ transform: Either<((Right) throws -> NextRight), Left>) -> LazyEither<Either<NextRight, Left>> {
// return LazyEither(self.next().apply(transform))
// }
// public func apply<NextRight>(_ transform: LazyEither<Either<((Right) throws -> NextRight), Left>>) -> LazyEither<Either<NextRight, Left>> {
// return LazyEither(self.next().apply(transform.next()))
// }
}
//
// Parser.swift
// ConfuciusKit
//
// Created by Seivan Heidari on 13/06/16.
//
//
import Foundation
#if os(iOS) || os(watchOS) || os(tvOS)
import UIKit
#else
import Cocoa
#endif
final public class Parser : ConfuciusKitDelegating {
public typealias ValidTopLevelContent = Array<Dictionary<String,AnyObject>>
public typealias FilteredContent = FileSystemLoaderFileContent<(raw:String, processed:ValidTopLevelContent)>
fileprivate typealias UnfilteredContent = FileSystemLoaderFileContent<(raw:String, processed:AnyObject)>
weak var delegate:ConfuciusKit!
internal init() {
}
public func parse(fromFile file:FileSystemLoader.File) throws -> Parser.FilteredContent {
return try Either<FileSystemLoader.File, Error>
.right(file)
.map(Parser.readContentOfFile)
.map(FileSystemLoaderFileContent.init)
.map(Parser.parsers)
.map(Parser.validateTopLevelArrayWithNestedDictionaries)
// .map(Parser.insertFilenameAsRootNamespaceIfNoRootPresent)
.unwrapError()
}
public func parse(fromFiles files:[FileSystemLoader.File]) -> [Either<Parser.FilteredContent, Error>] {
return files
.flatMap(Either<FileSystemLoader.File, Error>.right)
.flatMap { $0.map(self.parse) }
}
}
public extension Parser {
public enum ParseError : Error {
case missingParserForExtension(FileSystemLoader.File)
case failedParsingFromFileContent(FileSystemLoaderFileContent<String>, Error)
case failedEncodingFileContentToData(FileSystemLoaderFileContent<String>)
case invalidProcessedObjectShouldBeDictionary(FileSystemLoaderFileContent<(raw:String, processed:AnyObject)>)
}
}
fileprivate extension Parser {
static fileprivate func readContentOfFile(fromFile file:FileSystemLoader.File) throws -> (String, FileSystemLoader.File) {
return try (String(contentsOf: file.path), file)
}
static fileprivate func parsers(forFileContent fileContent:FileSystemLoaderFileContent<String>) throws -> Parser.UnfilteredContent {
switch fileContent.file.fileExtension {
case "yml", "yaml": return try parseYAML(fromFileContent: fileContent)
case "json": return try parseJSON(fromFileContent: fileContent)
default: throw Parser.ParseError.missingParserForExtension(fileContent.file)
}
}
static fileprivate func parseJSON(fromFileContent fileContent:FileSystemLoaderFileContent<String>) throws -> Parser.UnfilteredContent {
return try Either<FileSystemLoaderFileContent<String>, Error>
.right(fileContent)
.map { $0.content }
.map { $0.data(using: String.Encoding.utf8) }
.map { try $0.throwingIfNil(Parser.ParseError.failedEncodingFileContentToData(fileContent)) }
.map { try JSONSerialization.jsonObject(with: $0, options: []) }
.mapLeft { throw Parser.ParseError.failedParsingFromFileContent(fileContent, $0) }
.map { FileSystemLoaderFileContent(pathContent: (raw:fileContent.content, processed:$0 as AnyObject), from: fileContent.file) }
.unwrapError()
}
static fileprivate func parseYAML(fromFileContent fileContent:FileSystemLoaderFileContent<String>) throws -> Parser.UnfilteredContent {
return try Either<FileSystemLoaderFileContent<String>, Error>
.right(fileContent)
.map { $0.content }
.map(Yaml.yamlObject)
.map { FileSystemLoaderFileContent(pathContent: (raw:fileContent.content, processed:$0), from: fileContent.file) }
.unwrapError()
}
static fileprivate func validateTopLevelArrayWithNestedDictionaries(forFileContent fileContent:Parser.UnfilteredContent) throws -> Parser.FilteredContent {
switch fileContent.content.processed {
case let content as ValidTopLevelContent:
return FileSystemLoaderFileContent( pathContent: (raw:fileContent.content.raw, processed:content), from: fileContent.file)
default:
throw Parser.ParseError.invalidProcessedObjectShouldBeDictionary(fileContent)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment