source: launchers/macosx/I2PLauncher/routermgmt/Service.swift @ 7cb0c9b

Last change on this file since 7cb0c9b was 7cb0c9b, checked in by meeh <meeh@…>, 14 months ago

Mac OSX Launcher: Router management/healthcheck code updates.

  • Property mode set to 100644
File size: 3.6 KB
Line 
1//
2//  Service.swift
3//  I2PLauncher
4//
5//  Created by Mikal Villa on 12/04/2019.
6//  Copyright © 2019 The I2P Project. All rights reserved.
7//
8
9import Foundation
10
11public enum ServiceStatus: Int, Comparable {
12  case undetermined
13  case waiting
14  case started
15  case notice
16  case killed
17  case crashed
18  case stopped
19  case restarting
20 
21  public static func < (lhs: ServiceStatus, rhs: ServiceStatus) -> Bool {
22    return lhs.rawValue < rhs.rawValue
23  }
24}
25
26protocol ComparableStatus: Comparable {
27  var serviceStatus: ServiceStatus { get }
28}
29
30extension ComparableStatus {
31  public static func < (lhs: Self, rhs: Self) -> Bool {
32    return lhs.serviceStatus < rhs.serviceStatus
33  }
34}
35
36typealias Service = BaseService & RequiredServiceProperties
37
38protocol RequiredServiceProperties {
39  var name: String { get }
40  //var url: URL { get }
41}
42
43extension RequiredServiceProperties {
44  // Default implementation of the property `name` is to return the class name
45  var name: String { return "\(type(of: self))" }
46}
47
48public class BaseService {
49  public var status: ServiceStatus = .undetermined {
50    didSet {
51      if oldValue == .undetermined || status == .undetermined || oldValue == status {
52        self.shouldNotify = false
53      } else if Preferences.shared().notifyOnStatusChange {
54        self.shouldNotify = true
55      }
56    }
57  }
58  var message: String = "Loading…"
59  var shouldNotify = false
60 
61  public static func all() -> [BaseService] {
62    guard let servicesPlist = Bundle.main.path(forResource: "RouterServices", ofType: "plist"),
63      let services = NSDictionary(contentsOfFile: servicesPlist)?["services"] as? [String] else {
64        fatalError("The RouterServices.plist file does not exist. The build phase script might have failed.")
65    }
66   
67    return services.map(BaseService.named).compactMap { $0 }
68  }
69 
70  static func named(_ name: String) -> BaseService? {
71    return (NSClassFromString("I2PLauncher.\(name)") as? Service.Type)?.init()
72  }
73 
74  public required init() {}
75 
76  public func updateStatus(callback: @escaping (BaseService) -> Void) {}
77 
78  func _fail(_ error: Error?) {
79    self.status = .undetermined
80    self.message = error.debugDescription// ?? "Unexpected error"
81  }
82 
83  func _fail(_ message: String) {
84    self.status = .undetermined
85    self.message = message
86  }
87 
88  func notifyIfNecessary() {
89    guard let realSelf = self as? Service else { fatalError("BaseService should not be used directly.") }
90   
91    guard shouldNotify else { return }
92   
93    self.shouldNotify = false
94   
95    let notification = NSUserNotification()
96    let possessiveS = realSelf.name.hasSuffix("s") ? "'" : "'s"
97    notification.title = "\(realSelf.name)\(possessiveS) status has changed"
98    notification.informativeText = message
99   
100    NSUserNotificationCenter.default.deliver(notification)
101  }
102}
103
104extension BaseService: Equatable {
105  public static func == (lhs: BaseService, rhs: BaseService) -> Bool {
106    guard
107      let lhs = lhs as? Service,
108      let rhs = rhs as? Service
109      else {
110        fatalError("BaseService should not be used directly.")
111    }
112   
113    return lhs.name == rhs.name
114  }
115}
116
117extension BaseService: Comparable {
118  public static func < (lhs: BaseService, rhs: BaseService) -> Bool {
119    guard
120      let lhs = lhs as? Service,
121      let rhs = rhs as? Service
122      else {
123        fatalError("BaseService should not be used directly.")
124    }
125   
126    let sameStatus = lhs.status == rhs.status
127    let differentStatus =
128      lhs.status != .started && lhs.status != .notice
129        && rhs.status == .started || rhs.status == .notice
130    return ((lhs.name < rhs.name) && sameStatus) || differentStatus
131  }
132}
Note: See TracBrowser for help on using the repository browser.