-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add module FileCache and append CocoaLumberjack dependency
- Loading branch information
Showing
10 changed files
with
409 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"originHash" : "90eb498ae5930a04b7630d92315dd6af3c8b8f2d886297aed55966f330f14687", | ||
"pins" : [ | ||
{ | ||
"identity" : "cocoalumberjack", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/CocoaLumberjack/CocoaLumberjack.git", | ||
"state" : { | ||
"revision" : "4b8714a7fb84d42393314ce897127b3939885ec3", | ||
"version" : "3.8.5" | ||
} | ||
}, | ||
{ | ||
"identity" : "swift-log", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/apple/swift-log", | ||
"state" : { | ||
"revision" : "9cb486020ebf03bfa5b5df985387a14a98744537", | ||
"version" : "1.6.1" | ||
} | ||
} | ||
], | ||
"version" : 3 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// | ||
// JSONBuilder 2.swift | ||
// DailyDeeds | ||
// | ||
// Created by Дмитрий Корчагин on 6/17/24. | ||
// | ||
|
||
import Foundation | ||
|
||
@resultBuilder | ||
public struct CSVBuilder { | ||
public static func buildBlock(_ components: String...) -> String { | ||
return components.joined(separator: ",") | ||
} | ||
|
||
public static func buildExpression(_ expression: String) -> String { | ||
return expression.escapeSpecialCharacters(",") | ||
} | ||
|
||
public static func buildExpression(_ expression: String?) -> String { | ||
if let str = expression { | ||
return str.escapeSpecialCharacters(",") | ||
} else { | ||
return "" | ||
} | ||
} | ||
|
||
public static func buildExpression(_ expression: Date?) -> String { | ||
return expression.toString() | ||
} | ||
|
||
public static func buildExpression(_ expression: Bool) -> String { | ||
return expression.description | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// | ||
// CSVParsable.swift | ||
// DailyDeeds | ||
// | ||
// Created by Дмитрий Корчагин on 7/8/24. | ||
// | ||
|
||
import Foundation | ||
|
||
public protocol CSVParsable { | ||
var csv: String { get } | ||
static func parse(csv: String) -> Self? | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Дмитрий Корчагин on 7/8/24. | ||
// | ||
|
||
import Foundation | ||
|
||
extension Date { | ||
enum StripTimeType: CaseIterable { | ||
case days | ||
case minutes | ||
} | ||
|
||
static var calendar: Calendar = { | ||
var calendar = Calendar(identifier: .gregorian) | ||
calendar.firstWeekday = 2 | ||
return calendar | ||
}() | ||
|
||
var tomorrow: Date { | ||
return Calendar.current.date(byAdding: .day, value: 1, to: self) ?? self | ||
} | ||
|
||
func toString(format: String = "yyyy-MM-dd HH:mm:ss") -> String { | ||
let dateFormatter = DateFormatter() | ||
dateFormatter.dateFormat = format | ||
return dateFormatter.string(from: self) | ||
} | ||
|
||
func strip(to stripTimeType: Date.StripTimeType) -> Date { | ||
switch stripTimeType { | ||
case .days: | ||
let components = Date.calendar.dateComponents([.year, .month, .day], from: self) | ||
return Date.calendar.date(from: components) ?? self | ||
case .minutes: | ||
let components = Date.calendar.dateComponents([.year, .month, .day, .hour, .minute], from: self) | ||
return Date.calendar.date(from: components) ?? self | ||
} | ||
} | ||
} | ||
|
||
extension Optional where Wrapped == Date { | ||
func toString(format: String = "yyyy-MM-dd HH:mm:ss") -> String { | ||
switch self { | ||
case .none: | ||
return "" | ||
case .some(let wrapped): | ||
return wrapped.toString() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Дмитрий Корчагин on 7/8/24. | ||
// | ||
|
||
import Foundation | ||
|
||
public extension String { | ||
func toDate(format: String = "yyyy-MM-dd HH:mm:ss") -> Date? { | ||
let dateFormatter = DateFormatter() | ||
dateFormatter.dateFormat = format | ||
return dateFormatter.date(from: self) | ||
} | ||
|
||
func escapeSpecialCharacters(_ chars: Character...) -> String { | ||
return chars.reduce(into: self) { partialResult, char in | ||
partialResult = partialResult.replacingOccurrences(of: String(char), with: "\\\(char)") | ||
} | ||
} | ||
|
||
func unescapeSpecialCharacters(_ chars: Character...) -> String { | ||
return chars.reduce(into: self) { partialResult, char in | ||
partialResult = partialResult.replacingOccurrences(of: "\\\(char)", with: String(char)) | ||
} | ||
} | ||
|
||
func splitByUnescaped(separator: Character) -> [String] { | ||
// Создаем регулярное выражение для поиска разделителя, не предшествующего обратному слэшу | ||
let escapedSeparator = NSRegularExpression.escapedPattern(for: String(separator)) | ||
let pattern = "(?<!\\\\)" + escapedSeparator | ||
guard let regex = try? NSRegularExpression(pattern: pattern) else { return [] } | ||
|
||
let range = NSRange(self.startIndex..<self.endIndex, in: self) | ||
let matches = regex.matches(in: self, range: range) | ||
|
||
var results: [String] = [] | ||
var lastIndex = self.startIndex | ||
|
||
for match in matches { | ||
guard let matchRange = Range(match.range, in: self) else { continue } | ||
let substring = self[lastIndex..<matchRange.lowerBound] | ||
results.append(String(substring)) | ||
lastIndex = matchRange.upperBound | ||
} | ||
|
||
results.append(String(self[lastIndex...])) | ||
results = results.map { $0.unescapeSpecialCharacters(separator) } | ||
|
||
return results | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// | ||
// FileCache.swift | ||
// DailyDeeds | ||
// | ||
// Created by Дмитрий Корчагин on 7/8/24. | ||
// | ||
|
||
import Foundation | ||
import CocoaLumberjackSwift | ||
|
||
public class FileCache<T: JSONParsable & CSVParsable> { | ||
public enum FileFormat { | ||
case json | ||
case csv | ||
} | ||
|
||
public enum FileError: Error { | ||
case fileNotFound | ||
case dataCorrupted | ||
case parseFailed | ||
case writeToFileFailed | ||
case incorrectFileName | ||
case directoryNotFound | ||
case loadFromJSONFileFailed | ||
case loadFromCSVFileFailed | ||
case fileAlreadyExists | ||
case unknown | ||
} | ||
|
||
public func loadFromFile(named fileName: String, format: FileFormat) -> Result<[T], FileError> { | ||
do { | ||
let url = try getDocumentsDirectory().appendingPathComponent(fileName) | ||
guard FileManager.default.fileExists(atPath: url.path) else { return .failure(.fileNotFound) } | ||
|
||
let result: Result<[T], FileError> | ||
switch format { | ||
case .json: result = loadFromJSONFile(with: url) | ||
case .csv: result = loadFromCSVFile(with: url) | ||
} | ||
return result | ||
} catch let error as FileError { | ||
return .failure(error) | ||
} catch { | ||
return .failure(.unknown) | ||
} | ||
} | ||
|
||
private func loadFromJSONFile(with url: URL) -> Result<[T], FileError> { | ||
do { | ||
let data = try Data(contentsOf: url) | ||
guard let jsonArray = try JSONSerialization.jsonObject(with: data) as? [JSONDictionary] | ||
else { return .failure(.dataCorrupted) } | ||
|
||
let items = jsonArray.compactMap { T.parse(json: $0) } | ||
return .success(items) | ||
} catch { | ||
DDLogError("Failed to load from JSON file: \(error.localizedDescription)") | ||
return .failure(.loadFromJSONFileFailed) | ||
} | ||
} | ||
|
||
private func loadFromCSVFile(with url: URL) -> Result<[T], FileError> { | ||
do { | ||
let csvString = try String(contentsOf: url) | ||
let items = csvString.split(separator: "\n").compactMap { T.parse(csv: String($0)) } | ||
return .success(items) | ||
} catch { | ||
DDLogError("Failed to load from CSV file: \(error.localizedDescription)") | ||
return .failure(.loadFromCSVFileFailed) | ||
} | ||
} | ||
|
||
@discardableResult | ||
public func saveToFile(named fileName: String, items: [T], format: FileFormat = .json) -> FileError? { | ||
do { | ||
let url = try getDocumentsDirectory().appendingPathComponent(fileName) | ||
switch format { | ||
case .json: return saveToJSONFile(with: url, items: items) | ||
case .csv: return saveToCSVFile(with: url, items: items) | ||
} | ||
} catch let error as FileError { | ||
return error | ||
} catch { | ||
return .unknown | ||
} | ||
} | ||
|
||
private func saveToJSONFile(with url: URL, items: [T]) -> FileError? { | ||
do { | ||
let jsonArray = items.map { $0.json } | ||
let jsonData = try JSONSerialization.data(withJSONObject: jsonArray, options: .prettyPrinted) | ||
try jsonData.write(to: url) | ||
return nil | ||
} catch { | ||
DDLogError("Failed to save to JSON file: \(error.localizedDescription)") | ||
return .writeToFileFailed | ||
} | ||
} | ||
|
||
private func saveToCSVFile(with url: URL, items: [T]) -> FileError? { | ||
do { | ||
let csvString = items.map { $0.csv }.joined(separator: "\n") | ||
try csvString.write(to: url, atomically: true, encoding: .utf8) | ||
return nil | ||
} catch { | ||
DDLogError("Failed to save to CSV file: \(error.localizedDescription)") | ||
return .writeToFileFailed | ||
} | ||
} | ||
|
||
private func getDocumentsDirectory() throws -> URL { | ||
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) | ||
guard let first = urls.first else { throw FileError.directoryNotFound } | ||
return first | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// | ||
// JSONBuilder.swift | ||
// DailyDeeds | ||
// | ||
// Created by Дмитрий Корчагин on 6/17/24. | ||
// | ||
|
||
import Foundation | ||
|
||
@resultBuilder | ||
public struct JSONBuilder { | ||
public static func buildBlock(_ components: JSONDictionary...) -> JSONDictionary { | ||
components.reduce(into: JSONDictionary()) { result, dictionary in | ||
for (key, value) in dictionary { | ||
result[key] = value | ||
} | ||
} | ||
} | ||
|
||
public static func buildExpression(_ expression: (key: String, value: String)) -> JSONDictionary { | ||
return [expression.key: expression.value] | ||
} | ||
|
||
public static func buildExpression(_ expression: (key: String, value: String?)) -> JSONDictionary { | ||
if let str = expression.value { | ||
return [expression.key: str] | ||
} else { | ||
return [:] | ||
} | ||
} | ||
|
||
public static func buildExpression(_ expression: (key: String, value: Date?)) -> JSONDictionary { | ||
return [expression.key: expression.value.toString()] | ||
} | ||
|
||
public static func buildExpression(_ expression: (key: String, value: Bool)) -> JSONDictionary { | ||
return [expression.key: expression.value] | ||
} | ||
} |
Oops, something went wrong.