CrawlModels.swift
1 // 2 // CrawlModels.swift 3 // RacerTracer 4 // 5 // Created by Alexander Kunau on 29.09.25. 6 // 7 8 import Foundation 9 import SwiftUI 10 11 // MARK: - Crawling Models 12 13 struct CrawlSettings { 14 let maxDepth: Int 15 let maxConcurrentRequests: Int 16 let delayBetweenRequests: Double 17 let followRedirects: Bool 18 let respectRobotsTxt: Bool 19 let enableDeepLinkAnalysis: Bool 20 } 21 22 struct CrawledPage: Identifiable { 23 let id = UUID() 24 let url: String 25 let depth: Int 26 let title: String? 27 let statusCode: Int 28 let contentSize: Int? 29 let contentType: String? 30 let crawlError: String? 31 let timestamp: Date 32 let links: [String] 33 let images: [String] 34 35 var crawlDate: Date { 36 return timestamp 37 } 38 39 init(url: String, title: String?, depth: Int, crawlError: String?, contentSize: Int?, crawlDate: Date, statusCode: Int = 200, contentType: String? = nil, links: [String] = [], images: [String] = []) { 40 self.url = url 41 self.title = title 42 self.depth = depth 43 self.crawlError = crawlError 44 self.contentSize = contentSize 45 self.timestamp = crawlDate 46 self.statusCode = statusCode 47 self.contentType = contentType 48 self.links = links 49 self.images = images 50 } 51 } 52 53 struct CrawlResult { 54 let page: CrawledPage 55 let links: [DiscoveredLink] 56 } 57 58 struct DiscoveredLink { 59 let sourceUrl: String 60 let targetUrl: String 61 let linkText: String 62 let isInternal: Bool 63 let depth: Int 64 } 65 66 struct InternalLink: Identifiable { 67 let id = UUID() 68 let url: String 69 let text: String 70 let sourceUrl: String 71 let count: Int 72 let depth: Int 73 let linkType: LinkType 74 75 // Computed properties for compatibility with view 76 var targetURL: String { return url } 77 var anchorText: String { return text } 78 var sourceURL: String { return sourceUrl } 79 80 init(url: String, text: String, sourceUrl: String, count: Int, depth: Int = 1, linkType: LinkType = .dofollow) { 81 self.url = url 82 self.text = text 83 self.sourceUrl = sourceUrl 84 self.count = count 85 self.depth = depth 86 self.linkType = linkType 87 } 88 } 89 90 struct ExternalLink: Identifiable { 91 let id = UUID() 92 let url: String 93 let text: String 94 let sourceUrl: String 95 let domain: String 96 let count: Int 97 let linkType: LinkType 98 99 // Computed properties for compatibility with view 100 var targetURL: String { return url } 101 var anchorText: String { return text } 102 var sourceURL: String { return sourceUrl } 103 104 init(url: String, text: String, sourceUrl: String, domain: String, count: Int, linkType: LinkType = .dofollow) { 105 self.url = url 106 self.text = text 107 self.sourceUrl = sourceUrl 108 self.domain = domain 109 self.count = count 110 self.linkType = linkType 111 } 112 } 113 114 struct CrawlStatistics { 115 let totalPagesCrawled: Int 116 let totalLinksFound: Int 117 let internalLinksCount: Int 118 let externalLinksCount: Int 119 let crawlDuration: TimeInterval 120 let averagePageSize: Double 121 let errorCount: Int 122 let deepestLevelReached: Int 123 } 124 125 struct URLQueueItem { 126 let url: URL 127 let depth: Int 128 } 129 130 enum CrawlError: Error { 131 case invalidURL 132 case robotsTxtBlocked 133 case maxDepthExceeded 134 case networkError(String) 135 case parseError(String) 136 } 137 138 enum LinkType: CaseIterable { 139 case dofollow 140 case nofollow 141 case sponsored 142 case ugc 143 144 var description: String { 145 switch self { 146 case .dofollow: return "dofollow" 147 case .nofollow: return "nofollow" 148 case .sponsored: return "sponsored" 149 case .ugc: return "ugc" 150 } 151 } 152 153 var color: Color { 154 switch self { 155 case .dofollow: return .blue 156 case .nofollow: return .orange 157 case .sponsored: return .green 158 case .ugc: return .purple 159 } 160 } 161 } 162 163 // MARK: - Extensions 164 165 extension Array where Element == Int { 166 var averageForInts: Double { 167 guard !isEmpty else { return 0 } 168 return Double(reduce(0, +)) / Double(count) 169 } 170 } 171 172 extension Array where Element == Double { 173 var average: Double { 174 guard !isEmpty else { return 0 } 175 return reduce(0, +) / Double(count) 176 } 177 }