mirror of
				https://github.com/GayPizzaSpecifications/darwin-apk.git
				synced 2025-11-03 23:49:38 +00:00 
			
		
		
		
	decode index checksum too cus why not
also dumps prettier package descriptions to a file now wahoo I have good priorities
This commit is contained in:
		@ -8,12 +8,14 @@ let package = Package(
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  dependencies: [
 | 
					  dependencies: [
 | 
				
			||||||
    .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
 | 
					    .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
 | 
				
			||||||
 | 
					    .package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"),
 | 
				
			||||||
    .package(url: "https://github.com/tsolomko/SWCompression", from: "4.8.6"),
 | 
					    .package(url: "https://github.com/tsolomko/SWCompression", from: "4.8.6"),
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  targets: [
 | 
					  targets: [
 | 
				
			||||||
    .target(
 | 
					    .target(
 | 
				
			||||||
      name: "darwin-apk",
 | 
					      name: "darwin-apk",
 | 
				
			||||||
      dependencies: [
 | 
					      dependencies: [
 | 
				
			||||||
 | 
					        .product(name: "Algorithms", package: "swift-algorithms"),
 | 
				
			||||||
        .product(name: "SWCompression", package: "SWCompression"),
 | 
					        .product(name: "SWCompression", package: "SWCompression"),
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      path: "Sources/apk"),
 | 
					      path: "Sources/apk"),
 | 
				
			||||||
 | 
				
			|||||||
@ -29,3 +29,9 @@ extension ApkIndex {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension ApkIndex: CustomStringConvertible {
 | 
				
			||||||
 | 
					  var description: String {
 | 
				
			||||||
 | 
					    self.packages.map(String.init).joined(separator: "\n")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										116
									
								
								Sources/apk/Index/ApkIndexDigest.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								Sources/apk/Index/ApkIndexDigest.swift
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					// SPDX-License-Identifier: Apache-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					import Algorithms
 | 
				
			||||||
 | 
					import CryptoKit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ApkIndexDigest {
 | 
				
			||||||
 | 
					  let type: DigestType
 | 
				
			||||||
 | 
					  let data: Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  init?(type: DigestType, data: Data) {
 | 
				
			||||||
 | 
					    let len = switch type {
 | 
				
			||||||
 | 
					      case .md5: 16
 | 
				
			||||||
 | 
					      case .sha1: 20
 | 
				
			||||||
 | 
					      case .sha256: 32
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    guard len == data.count else {
 | 
				
			||||||
 | 
					      return nil
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    self.type = type
 | 
				
			||||||
 | 
					    self.data = data
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  init?(decode: String) {
 | 
				
			||||||
 | 
					    enum Encoding { case hex, base64 }
 | 
				
			||||||
 | 
					    let getEncoding = { (c: Character) -> Encoding? in
 | 
				
			||||||
 | 
					      switch c {
 | 
				
			||||||
 | 
					      case "Q": .base64
 | 
				
			||||||
 | 
					      case "X": .hex
 | 
				
			||||||
 | 
					      default: nil
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let getDigestType = { (c: Character) -> DigestType? in
 | 
				
			||||||
 | 
					      switch c {
 | 
				
			||||||
 | 
					      case "1": .sha1
 | 
				
			||||||
 | 
					      case "2": .sha256
 | 
				
			||||||
 | 
					      default: nil
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let hexData = { (from: Substring) -> Data? in
 | 
				
			||||||
 | 
					      // Ensure even number of characters
 | 
				
			||||||
 | 
					      guard from.count & ~0x1 == from.count else { return nil }
 | 
				
			||||||
 | 
					      //FIXME: will explode on encountering non-hexadecimal characters, works for now tho
 | 
				
			||||||
 | 
					      return Data(from.map(\.hexDigitValue).lazy
 | 
				
			||||||
 | 
					        .chunks(ofCount: 2)
 | 
				
			||||||
 | 
					        .map { UInt8($0.last!! + $0.first!! << 4) })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if decode.count < 2 {
 | 
				
			||||||
 | 
					      return nil
 | 
				
			||||||
 | 
					    } else if _slowPath(decode.first!.isHexDigit) {
 | 
				
			||||||
 | 
					      // Legacy MD5 hex digest mode
 | 
				
			||||||
 | 
					      guard decode.count != 32, let decoded = hexData(decode[...]) else {
 | 
				
			||||||
 | 
					        return nil
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      self.init(type: .md5, data: decoded)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // First two characters are a letter for the encoding type:
 | 
				
			||||||
 | 
					      // - 'X': hex digest
 | 
				
			||||||
 | 
					      // - 'Q': base64 string
 | 
				
			||||||
 | 
					      // ...and a number for the hash digest type:
 | 
				
			||||||
 | 
					      // - '1': SHA-1
 | 
				
			||||||
 | 
					      // - '2': SHA-256 (SHA-2)
 | 
				
			||||||
 | 
					      guard
 | 
				
			||||||
 | 
					        let encoding = getEncoding(decode.first!),
 | 
				
			||||||
 | 
					        let digest = getDigestType(decode[decode.index(after: decode.startIndex)])
 | 
				
			||||||
 | 
					      else { return nil }
 | 
				
			||||||
 | 
					      let dataString = decode[decode.index(decode.startIndex, offsetBy: 2)...]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // The remaining characters are the encoded digest
 | 
				
			||||||
 | 
					      var decoded: Data? = nil
 | 
				
			||||||
 | 
					      if _fastPath(encoding == .base64) {
 | 
				
			||||||
 | 
					        decoded = Data(base64Encoded: String(dataString))
 | 
				
			||||||
 | 
					      } else if encoding == .hex {
 | 
				
			||||||
 | 
					        decoded = hexData(dataString)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      guard let decoded = decoded else {
 | 
				
			||||||
 | 
					        return nil
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      self.init(type: digest, data: decoded)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension ApkIndexDigest: Equatable, Hashable {
 | 
				
			||||||
 | 
					  @inlinable static func == (lhs: Self, rhs: Self) -> Bool {
 | 
				
			||||||
 | 
					    lhs.type == rhs.type && lhs.data == rhs.data
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  func hash(into hasher: inout Hasher) {
 | 
				
			||||||
 | 
					    //self.type.hash(into: &hasher)
 | 
				
			||||||
 | 
					    self.data.hash(into: &hasher)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension ApkIndexDigest {
 | 
				
			||||||
 | 
					  enum DigestType {
 | 
				
			||||||
 | 
					    case md5, sha1, sha256
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension ApkIndexDigest.DigestType: CustomStringConvertible {
 | 
				
			||||||
 | 
					  var description: String {
 | 
				
			||||||
 | 
					    switch self {
 | 
				
			||||||
 | 
					    case .md5: "MD5"
 | 
				
			||||||
 | 
					    case .sha1: "SHA-1"
 | 
				
			||||||
 | 
					    case .sha256: "SHA-256"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension ApkIndexDigest: CustomStringConvertible {
 | 
				
			||||||
 | 
					  var description: String { "[\(self.type)] \(self.data.map { String(format: "%02X", $0) }.joined())" }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
import Foundation
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ApkIndexPackage: Hashable {
 | 
					struct ApkIndexPackage: Hashable {
 | 
				
			||||||
  let indexChecksum: String  //TODO: Decode cus why not
 | 
					  let indexChecksum: ApkIndexDigest
 | 
				
			||||||
  let name: String
 | 
					  let name: String
 | 
				
			||||||
  let version: String
 | 
					  let version: String
 | 
				
			||||||
  let architecture: String?
 | 
					  let architecture: String?
 | 
				
			||||||
@ -30,7 +30,7 @@ struct ApkIndexPackage: Hashable {
 | 
				
			|||||||
extension ApkIndexPackage {
 | 
					extension ApkIndexPackage {
 | 
				
			||||||
  init(raw rawEntry: ApkRawIndexEntry) throws(Self.ParseError) {
 | 
					  init(raw rawEntry: ApkRawIndexEntry) throws(Self.ParseError) {
 | 
				
			||||||
    // Required fields
 | 
					    // Required fields
 | 
				
			||||||
    var indexChecksum: String? = nil
 | 
					    var indexChecksum: ApkIndexDigest? = nil
 | 
				
			||||||
    var name: String? = nil
 | 
					    var name: String? = nil
 | 
				
			||||||
    var version: String? = nil
 | 
					    var version: String? = nil
 | 
				
			||||||
    var description: String? = nil
 | 
					    var description: String? = nil
 | 
				
			||||||
@ -70,7 +70,10 @@ extension ApkIndexPackage {
 | 
				
			|||||||
        do { dependencies = try ApkIndexDependency.extract(record.value) }
 | 
					        do { dependencies = try ApkIndexDependency.extract(record.value) }
 | 
				
			||||||
        catch { throw .badValue(key: record.key) }
 | 
					        catch { throw .badValue(key: record.key) }
 | 
				
			||||||
      case "C":
 | 
					      case "C":
 | 
				
			||||||
        indexChecksum = record.value  // base64-encoded SHA1 hash prefixed with "Q1"
 | 
					        guard let digest = ApkIndexDigest(decode: record.value) else {
 | 
				
			||||||
 | 
					          throw .badValue(key: record.key)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        indexChecksum = digest
 | 
				
			||||||
      case "S":
 | 
					      case "S":
 | 
				
			||||||
        guard let value = UInt64(record.value, radix: 10) else {
 | 
					        guard let value = UInt64(record.value, radix: 10) else {
 | 
				
			||||||
          throw .badValue(key: record.key)
 | 
					          throw .badValue(key: record.key)
 | 
				
			||||||
@ -152,7 +155,45 @@ extension ApkIndexPackage {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extension ApkIndexPackage: CustomStringConvertible {
 | 
					extension ApkIndexPackage: CustomStringConvertible {
 | 
				
			||||||
  var description: String { "pkg(\(self.name))" }
 | 
					  var description: String {
 | 
				
			||||||
 | 
					    var s = String()
 | 
				
			||||||
 | 
					    s += "index checksum: \(self.indexChecksum)\n"
 | 
				
			||||||
 | 
					    s += "name: --------- \(self.name)\n"
 | 
				
			||||||
 | 
					    s += "version: ------ \(self.version)\n"
 | 
				
			||||||
 | 
					    if let architecture = self.architecture {
 | 
				
			||||||
 | 
					    s += "architecture: - \(architecture)\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    s += "package size: - \(self.packageSize) byte(s) (\(self.packageSize.formatted(.byteCount(style: .file))))\n"
 | 
				
			||||||
 | 
					    s += "installed size: \(self.installedSize) byte(s) (\(self.installedSize.formatted(.byteCount(style: .file))))\n"
 | 
				
			||||||
 | 
					    s += "description: -- \(self.packageDescription)\n"
 | 
				
			||||||
 | 
					    s += "url: ---------- \(self.url)\n"
 | 
				
			||||||
 | 
					    s += "license: ------ \(self.license)\n"
 | 
				
			||||||
 | 
					    if let origin = self.origin {
 | 
				
			||||||
 | 
					    s += "origin: ------- \(origin)\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if let maintainer = self.maintainer {
 | 
				
			||||||
 | 
					    s += "maintainer: --- \(maintainer)\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if let buildTime = self.buildTime {
 | 
				
			||||||
 | 
					    s += "build time: --- \(buildTime)\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if let commit = self.commit {
 | 
				
			||||||
 | 
					    s += "commit: ------- \(commit)\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if let providerPrio = self.providerPriority {
 | 
				
			||||||
 | 
					    s += "provider prio:  \(providerPrio)\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if !self.dependencies.isEmpty {
 | 
				
			||||||
 | 
					    s += "dependencies: - \(self.dependencies.map(String.init).joined(separator: " "))\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if !self.provides.isEmpty {
 | 
				
			||||||
 | 
					    s += "provides: ----- \(self.provides.map { $0.name }.joined(separator: " "))\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if !self.installIf.isEmpty {
 | 
				
			||||||
 | 
					    s += "install if: --- \(self.installIf.map { $0.name }.joined(separator: " "))\n"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return s
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fileprivate extension Optional {
 | 
					fileprivate extension Optional {
 | 
				
			||||||
 | 
				
			|||||||
@ -42,13 +42,10 @@ public struct ApkIndexUpdater {
 | 
				
			|||||||
    do {
 | 
					    do {
 | 
				
			||||||
      let tables = try repositories.map { try readIndex(URL(filePath: $0.localName)) }
 | 
					      let tables = try repositories.map { try readIndex(URL(filePath: $0.localName)) }
 | 
				
			||||||
      index = ApkIndex.merge(tables)
 | 
					      index = ApkIndex.merge(tables)
 | 
				
			||||||
 | 
					      try index.description.write(to: URL(fileURLWithPath: "packages.txt"), atomically: false, encoding: .utf8)
 | 
				
			||||||
    } catch {
 | 
					    } catch {
 | 
				
			||||||
      fatalError(error.localizedDescription)
 | 
					      fatalError(error.localizedDescription)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    for package in index.packages {
 | 
					 | 
				
			||||||
      print("\(package.name):", package.dependencies)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private func readIndex(_ indexURL: URL) throws -> ApkIndex {
 | 
					  private func readIndex(_ indexURL: URL) throws -> ApkIndex {
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user