<commit_before>//
//  CacheRenderer.swift
//  AsyncImageView
//
//  Created by <NAME> on 11/22/15.
//  Copyright © 2015 <NAME>. All rights reserved.
//

import UIKit
import ReactiveCocoa
import Result

/// Decorates a `RendererType` to introduce a layer of caching.
public final class CacheRenderer<
	Renderer: RendererType,
	Cache: CacheType
	where
	Cache.Key == Renderer.Data,
	Cache.Value == UIImage
>: RendererType {
	private let renderer: Renderer
	private let cache: Cache

	public init(renderer: Renderer, cache: Cache) {
		self.renderer = renderer
		self.cache = cache
	}

	/// Returns an image from the cache if found,
	/// otherwise it invokes the decorated `renderer` and caches the result.
	public func renderImageWithData(data: Renderer.Data) -> SignalProducer<ImageResult, Renderer.Error> {
		return SignalProducer
			.attempt { [cache = self.cache] in
				return Result(
					cache.valueForKey(data)?.asCacheHit,
					failWith: CacheRendererError.ImageNotFound
				)
			}
			.startOn(QueueScheduler())
			.flatMapError { [renderer = self.renderer] _ in
				return renderer
					.renderImageWithData(data)
					.on(next: { [cache = self.cache] result in
						cache.setValue(result.image, forKey: data)
					})
					.map { $0.image.asCacheMiss }
			}
	}
}

extension RendererType {
	/// Surrounds this renderer with a layer of caching.
	public func withCache<
		Cache: CacheType
		where Cache.Key == Self.Data, Cache.Value == UIImage
		>(cache: Cache) -> CacheRenderer<Self, Cache>
	{
		return CacheRenderer(renderer: self, cache: cache)
	}
}

extension RenderDataType where Self: DataFileType {
	/// The default subdirectory for `RenderDataType`s that
	/// implement `DataFileType` is its size.
	///
	/// This can be overriden to return `nil`.
	public var subdirectory: String? {
		return subdirectoryForSize(self.size)
	}
}

internal func subdirectoryForSize(size: CGSize) -> String {
	return String(format: "%.2fx%.2f", size.width, size.height)
}

private enum CacheRendererError: ErrorType {
	case ImageNotFound
}

extension UIImage {
	private var asCacheHit: ImageResult {
		return ImageResult(
			image: self,
			cacheHit: true
		)
	}

	private var asCacheMiss: ImageResult {
		return ImageResult(
			image: self,
			cacheHit: false
		)
	}
}

<commit_msg>CacheRenderer now supports any Renderer, not just those producing UIImage<commit_after>//
//  CacheRenderer.swift
//  AsyncImageView
//
//  Created by <NAME> on 11/22/15.
//  Copyright © 2015 <NAME>. All rights reserved.
//

import UIKit
import ReactiveCocoa
import Result

/// Decorates a `RendererType` to introduce a layer of caching.
public final class CacheRenderer<
	Renderer: RendererType,
	Cache: CacheType
	where
	Cache.Key == Renderer.Data,
	Cache.Value == Renderer.RenderResult
>: RendererType {
	private let renderer: Renderer
	private let cache: Cache

	public init(renderer: Renderer, cache: Cache) {
		self.renderer = renderer
		self.cache = cache
	}

	/// Returns an image from the cache if found,
	/// otherwise it invokes the decorated `renderer` and caches the result.
	public func renderImageWithData(data: Renderer.Data) -> SignalProducer<ImageResult, Renderer.Error> {
		return SignalProducer
			.attempt { [cache = self.cache] in
				return Result(
					cache.valueForKey(data)?.image.asCacheHit,
					failWith: CacheRendererError.ImageNotFound
				)
			}
			.startOn(QueueScheduler())
			.flatMapError { [renderer = self.renderer] _ in
				return renderer
					.renderImageWithData(data)
					.on(next: { [cache = self.cache] result in
						cache.setValue(result, forKey: data)
					})
					.map { $0.image.asCacheMiss }
			}
	}
}

extension RendererType {
	/// Surrounds this renderer with a layer of caching.
	public func withCache<
		Cache: CacheType
		where
		Cache.Key == Self.Data,
		Cache.Value == Self.RenderResult
		>(cache: Cache) -> CacheRenderer<Self, Cache>
	{
		return CacheRenderer(renderer: self, cache: cache)
	}
}

extension RenderDataType where Self: DataFileType {
	/// The default subdirectory for `RenderDataType`s that
	/// implement `DataFileType` is its size.
	///
	/// This can be overriden to return `nil`.
	public var subdirectory: String? {
		return subdirectoryForSize(self.size)
	}
}

internal func subdirectoryForSize(size: CGSize) -> String {
	return String(format: "%.2fx%.2f", size.width, size.height)
}

private enum CacheRendererError: ErrorType {
	case ImageNotFound
}

extension UIImage {
	private var asCacheHit: ImageResult {
		return ImageResult(
			image: self,
			cacheHit: true
		)
	}

	private var asCacheMiss: ImageResult {
		return ImageResult(
			image: self,
			cacheHit: false
		)
	}
}


{'fraction_non_alphanumeric': 0.10500215610176801, 'fraction_numerical': 0.00560586459680897, 'mean_word_length': 3.5749506903353057, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2756363', 'n_tokens_mistral': 1619, 'n_tokens_neox': 1455, 'n_words': 429}