/ RacerTracer / Models / CrawlModels.swift
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  }