Skip to content

Commit

Permalink
Leif/2.0.0 (#12)
Browse files Browse the repository at this point in the history
* Update with changes for 2.0.0

* Remove unused workflow

* Update PropertyWrappersTests

* Remove ComposableCache from windows

* Remove AnyCacheable from windows

* Fix test to handle windows

* Update test case

* Add Logger typealias and Logging wrapper (#11)

* Update README
  • Loading branch information
0xLeif committed Jul 7, 2023
1 parent d9be5df commit d6d6325
Show file tree
Hide file tree
Showing 13 changed files with 1,006 additions and 64 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Add the following line to your `Package.swift` file in the dependencies array:

```swift
dependencies: [
.package(url: "https://github.com/0xLeif/Cache.git", from: "1.0.0")
.package(url: "https://github.com/0xLeif/Cache.git", from: "2.0.0")
]
```

Expand Down
153 changes: 153 additions & 0 deletions Sources/Cache/Cache/AnyCacheable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#if !os(Windows)
public class AnyCacheable: Cacheable {
public typealias Key = AnyHashable
public typealias Value = Any

private var cache: any Cacheable

private var cacheGet: ((AnyHashable) -> Any?)!
private var cacheResolve: ((AnyHashable) throws -> Any)!
private var cacheSet: ((Any, AnyHashable) -> Void)!
private var cacheRemove: ((AnyHashable) -> Void)!
private var cacheContains: ((AnyHashable) -> Bool)!
private var cacheRequireKeys: ((Set<AnyHashable>) throws -> Void)!
private var cacheRequireKey: ((AnyHashable) throws -> Void)!
private var cacheValues: (() -> [AnyHashable: Any])!

public init<InnerCache: Cacheable>(_ cache: InnerCache) {
self.cache = cache

self.cacheGet = { key in
guard let key = key as? InnerCache.Key else { return nil }

return cache.get(key, as: Any.self)
}

self.cacheResolve = { key in
guard
let key = key as? InnerCache.Key
else { throw MissingRequiredKeysError(keys: [key]) }

return try cache.resolve(key, as: Any.self)
}

self.cacheSet = { value, key in
guard
let value = value as? InnerCache.Value,
let key = key as? InnerCache.Key
else { return }

var mutableCache = cache

mutableCache.set(value: value, forKey: key)

self.cache = mutableCache
}

self.cacheRemove = { key in
guard let key = key as? InnerCache.Key else { return }

var mutableCache = cache

mutableCache.remove(key)

self.cache = mutableCache
}

self.cacheContains = { key in
guard
let key = key as? InnerCache.Key
else { return false }

return cache.contains(key)
}

self.cacheRequireKeys = { keys in
let validKeys: Set<InnerCache.Key> = Set(keys.compactMap { $0 as? InnerCache.Key })

guard
validKeys.count == keys.count
else { throw MissingRequiredKeysError(keys: keys.subtracting(validKeys)) }

_ = try cache.require(keys: validKeys)
}

self.cacheRequireKey = { key in
guard
let key = key as? InnerCache.Key
else { throw MissingRequiredKeysError(keys: [key]) }

_ = try cache.require(key)
}

self.cacheValues = {
cache.values(ofType: Any.self)
}
}

required public convenience init(initialValues: [AnyHashable: Any]) {
self.init(Cache(initialValues: initialValues))
}

public func get<Output>(
_ key: AnyHashable,
as: Output.Type = Output.self
) -> Output? {
guard let value = cacheGet(key) else {
return nil
}

guard let output = value as? Output else {
return nil
}

return output
}

public func resolve<Output>(
_ key: AnyHashable,
as: Output.Type = Output.self
) throws -> Output {
let resolvedValue = try cacheResolve(key)

guard let output = resolvedValue as? Output else {
throw InvalidTypeError(
expectedType: Output.self,
actualType: type(of: get(key, as: Any.self))
)
}

return output
}

public func set(value: Value, forKey key: AnyHashable) {
cacheSet(value, key)
}

public func remove(_ key: AnyHashable) {
cacheRemove(key)
}

public func contains(_ key: AnyHashable) -> Bool {
cacheContains(key)
}

public func require(keys: Set<AnyHashable>) throws -> Self {
try cacheRequireKeys(keys)

return self
}

public func require(_ key: AnyHashable) throws -> Self {
try cacheRequireKey(key)

return self
}

public func values<Output>(ofType: Output.Type) -> [AnyHashable: Output] {
cacheValues().compactMapValues { value in
value as? Output
}
}
}
#endif
25 changes: 25 additions & 0 deletions Sources/Cache/Cache/Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,28 @@ open class Cache<Key: Hashable, Value>: Cacheable {
#if !os(Linux) && !os(Windows)
extension Cache: ObservableObject { }
#endif

extension Cache {
/**
Gets a value from the cache for a given key.
- Parameters:
- key: The key to retrieve the value for.
- Returns: The value stored in cache for the given key, or `nil` if it doesn't exist.
*/
public func get(_ key: Key) -> Value? {
get(key, as: Value.self)
}

/**
Resolves a value from the cache for a given key.
- Parameters:
- key: The key to retrieve the value for.
- Returns: The value stored in cache for the given key.
- Throws: `MissingRequiredKeysError` if the key is missing, or `InvalidTypeError` if the value type is not compatible with the expected type.
*/
public func resolve(_ key: Key) throws -> Value {
try resolve(key, as: Value.self)
}
}
99 changes: 99 additions & 0 deletions Sources/Cache/Cache/ComposableCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#if !os(Windows)
public struct ComposableCache<Key: Hashable>: Cacheable {
private let caches: [AnyCacheable]

public init(caches: [any Cacheable]) {
self.caches = caches.map { AnyCacheable($0) }
}

public init(initialValues: [Key: Any]) {
self.init(caches: [Cache(initialValues: initialValues)])
}

public func get<Output>(
_ key: Key,
as: Output.Type = Output.self
) -> Output? {
for cache in caches {
guard
let output = cache.get(key, as: Output.self)
else {
continue
}

return output
}

return nil
}

public func resolve<Output>(
_ key: Key,
as: Output.Type = Output.self
) throws -> Output {
for cache in caches {
guard
let output = try? cache.resolve(key, as: Output.self)
else {
continue
}

return output
}

throw MissingRequiredKeysError(keys: [key])
}

public func set(value: Any, forKey key: Key) {
for cache in caches {
cache.set(value: value, forKey: key)
}
}

public func remove(_ key: Key) {
for cache in caches {
cache.remove(key)
}
}

public func contains(_ key: Key) -> Bool {
for cache in caches {
if cache.contains(key) {
return true
}
}

return false
}

public func require(keys: Set<Key>) throws -> ComposableCache<Key> {
for cache in caches {
_ = try cache.require(keys: keys)
}

return self
}

public func require(_ key: Key) throws -> ComposableCache<Key> {
for cache in caches {
_ = try cache.require(key)
}

return self
}

public func values<Output>(ofType: Output.Type) -> [Key: Output] {
for cache in caches {
let values = cache.values(ofType: Output.self).compactMapKeys { $0 as? Key }

guard values.keys.count != 0 else {
continue
}

return values
}

return [:]
}
}
#endif
11 changes: 3 additions & 8 deletions Sources/Cache/Cache/RequiredKeysCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,9 @@ public class RequiredKeysCache<Key: Hashable, Value>: Cache<Key, Value> {
- Throws: A runtime error if the required key is not present in the cache or if the expected value type is incorrect.
*/
public func resolve<Output>(requiredKey: Key, as: Output.Type = Output.self) -> Output {
guard
requiredKeys.contains(requiredKey)
else { fatalError("The key '\(requiredKey)' is not a Required Key.") }

guard
contains(requiredKey)
else { fatalError("Required Key Missing: '\(requiredKey)'") }

precondition(requiredKeys.contains(requiredKey), "The key '\(requiredKey)' is not a Required Key.")
precondition(contains(requiredKey), "Required Key Missing: '\(requiredKey)'")

do {
return try resolve(requiredKey, as: Output.self)
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/Cache/CacheInitializable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public protocol CacheInitializable {
associatedtype OriginCache: Cacheable

init(cache: OriginCache)
}
23 changes: 0 additions & 23 deletions Sources/Cache/Cacheable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,27 +70,4 @@ public extension Cacheable {
var allValues: [Key: Value] {
values(ofType: Value.self)
}

/**
Gets a value from the cache for a given key.
- Parameters:
- key: The key to retrieve the value for.
- Returns: The value stored in cache for the given key, or `nil` if it doesn't exist.
*/
func get(_ key: Key) -> Value? {
get(key, as: Value.self)
}

/**
Resolves a value from the cache for a given key.
- Parameters:
- key: The key to retrieve the value for.
- Returns: The value stored in cache for the given key.
- Throws: `MissingRequiredKeysError` if the key is missing, or `InvalidTypeError` if the value type is not compatible with the expected type.
*/
func resolve(_ key: Key) throws -> Value {
try resolve(key, as: Value.self)
}
}

0 comments on commit d6d6325

Please sign in to comment.