-
-
Notifications
You must be signed in to change notification settings - Fork 38
/
Score.swift
129 lines (110 loc) · 4.29 KB
/
Score.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
enum Score {
struct Input {
var licenseKind: License.Kind
var releaseCount: Int
var likeCount: Int
var isArchived: Bool
var numberOfDependencies: Int?
var lastActivityAt: Date?
var hasDocumentation: Bool
var numberOfContributors: Int
}
static func compute(_ candidate: Input) -> Int {
var score = 0
// Is the package archived and no longer receiving updates?
if candidate.isArchived == false { score += 20 }
// Is the license open-source and compatible with the App Store?
switch candidate.licenseKind {
case .compatibleWithAppStore: score += 10
case .incompatibleWithAppStore: score += 3
default: break;
}
// Number of releases
switch candidate.releaseCount {
case ..<5 : break
case 5..<20: score += 10
default : score += 20
}
// Stars count
switch candidate.likeCount {
case ..<25 : break
case 25..<100 : score += 10
case 100..<500 : score += 20
case 500..<5_000 : score += 30
case 5_000..<10_000: score += 35
default: score += 37
}
// Number of resolved dependencies
switch candidate.numberOfDependencies {
case .some(..<3): score += 5
case .some(3..<5): score += 2
default: break
}
// Last maintenance activity
if let lastActivityAt = candidate.lastActivityAt {
// Note: This is not the most accurate method to calculate the number of days between
// two dates, but is more than good enough for the purposes of this calculation.
let dateDifference = Calendar.current.dateComponents([.day], from: lastActivityAt, to: Current.date())
switch dateDifference.day {
case .some(..<30) : score += 15
case .some(30..<180) : score += 10
case .some(180..<360) : score += 5
default: break
}
}
if candidate.hasDocumentation {
score += 15
}
switch candidate.numberOfContributors {
case ..<5: break
case 5..<20: score += 5
default: score += 10
}
return score
}
static func compute(package: Joined<Package, Repository>, versions: [Version]) -> Int {
guard
let defaultVersion = versions.latest(for: .defaultBranch),
let repo = package.repository
else { return 0 }
let hasDocumentation = [
defaultVersion,
versions.latest(for: .release),
versions.latest(for: .preRelease)
].hasDocumentation()
let numberOfContributors = {
guard let authors = repo.authors else { return 0 }
return authors.authors.count + authors.numberOfContributors
}()
return Score.compute(
.init(licenseKind: repo.license.licenseKind,
releaseCount: versions.releases.count,
likeCount: repo.stars,
isArchived: repo.isArchived,
numberOfDependencies: defaultVersion.resolvedDependencies?.count,
lastActivityAt: repo.lastActivityAt,
hasDocumentation: hasDocumentation,
numberOfContributors: numberOfContributors)
)
}
}
private extension Array where Element == Version {
func latest(for kind: Version.Kind) -> Version? {
first { $0.latest == kind }
}
var releases: Self { filter { $0.reference.isTag } }
}