// Row.swift // This file is part of KeePass.swift // // Copyright © 2021 Maxime Epain. All rights reserved. // // KeePass.swift is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // KeePass.swift is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with KeePass.swift. If not, see . import Binary import Foundation public protocol Row: AnyObject { associatedtype Column var properties: [TLV] { get set } } extension Row where Self: Writable, Column: Writable { public func write(to output: Output) throws { try output.write(properties) } } extension Row where Column: Equatable { public subscript(_ column: Column) -> Bytes? { get { properties.first(where: { $0.type == column })?.value } set { properties.removeAll(column) guard let value = newValue else { return } let tlv = TLV(type: column, value: value) properties.insert(tlv, at: 0) } } public func set(_ property: TLV) { properties.removeAll(property.type) properties.insert(property, at: 0) } public func set(_ date: Date, at column: Column) { self[column] = Database.bytes(from: date) } public func date(at column: Column) -> Date? { guard let bytes = self[column] else { return nil } return Database.date(from: bytes) } public subscript(_ column: Column) -> T? where T: LosslessBytesConvertible { get { T?(self[column]) } set { self[column] = newValue?.bytes } } public func remove(_ column: Column) { properties.removeAll(column) } } extension Sequence where Element: Row, Element.Column: Equatable { public func first(column: Element.Column, where predicate: (T) throws -> Bool) throws -> Element? where T: LosslessBytesConvertible { try first(where: { guard let bytes = $0[column] else { return false } return try predicate(T(bytes)) }) } public func sorted(column: Element.Column, by areInIncreasingOrder: (T, T) throws -> Bool) throws -> [Element] where T: LosslessBytesConvertible { try sorted(by: { guard let rhs = $0[column], let lhs = $1[column] else { return false } return try areInIncreasingOrder(T(rhs), T(lhs)) }) } }