Skip to content

Commit 5380a41

Browse files
committed
refactor stats counters types, and add ordered dictionaries
1 parent ecb92ac commit 5380a41

File tree

5 files changed

+113
-91
lines changed

5 files changed

+113
-91
lines changed

Package.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,9 @@ let package:Package = .init(
581581
.target(name: "MD5"),
582582
.target(name: "SymbolGraphs"),
583583
.target(name: "UnidocAPI"),
584+
585+
.product(name: "OrderedCollections", package: "swift-collections"),
586+
.product(name: "BSON_OrderedCollections", package: "swift-mongodb"),
584587
]),
585588

586589
.target(name: "UnixTime"),

Sources/UnidocRecords/Volumes/Stats/Unidoc.Stats.Coverage.swift

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,34 +32,26 @@ extension Unidoc.Stats.Coverage:ExpressibleByDictionaryLiteral
3232
self.init(undocumented: 0, indirect: 0, direct: 0)
3333
}
3434
}
35-
extension Unidoc.Stats.Coverage
35+
extension Unidoc.Stats.Coverage:Unidoc.StatsCounters,
36+
BSONDocumentEncodable,
37+
BSONDocumentDecodable
3638
{
3739
@frozen public
38-
enum CodingKey:String, Sendable
40+
enum CodingKey:String, Sendable, CaseIterable
3941
{
4042
case undocumented = "U"
4143
case indirect = "I"
4244
case direct = "D"
4345
}
44-
}
45-
extension Unidoc.Stats.Coverage:BSONDocumentEncodable
46-
{
47-
public
48-
func encode(to bson:inout BSON.DocumentEncoder<CodingKey>)
49-
{
50-
bson[.undocumented] = self.undocumented != 0 ? self.undocumented : nil
51-
bson[.indirect] = self.indirect != 0 ? self.indirect : nil
52-
bson[.direct] = self.direct != 0 ? self.direct : nil
53-
}
54-
}
55-
extension Unidoc.Stats.Coverage:BSONDocumentDecodable
56-
{
57-
@inlinable public
58-
init(bson:BSON.DocumentDecoder<CodingKey>) throws
46+
47+
@inlinable public static
48+
subscript(key:CodingKey) -> WritableKeyPath<Self, Int>
5949
{
60-
self.init(
61-
undocumented: try bson[.undocumented]?.decode() ?? 0,
62-
indirect: try bson[.indirect]?.decode() ?? 0,
63-
direct: try bson[.direct]?.decode() ?? 0)
50+
switch key
51+
{
52+
case .undocumented: \.undocumented
53+
case .indirect: \.indirect
54+
case .direct: \.direct
55+
}
6456
}
6557
}

Sources/UnidocRecords/Volumes/Stats/Unidoc.Stats.Decl.swift

Lines changed: 24 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -118,32 +118,12 @@ extension Unidoc.Stats.Decl:ExpressibleByDictionaryLiteral
118118
attachedMacros: 0)
119119
}
120120
}
121-
extension Unidoc.Stats.Decl
122-
{
123-
@inlinable public
124-
var total:Int
125-
{
126-
self.typealiases
127-
+ self.structures
128-
+ self.protocols
129-
+ self.classes
130-
+ self.actors
131-
+ self.requirements
132-
+ self.witnesses
133-
+ self.constructors
134-
+ self.subscripts
135-
+ self.functors
136-
+ self.methods
137-
+ self.operators
138-
+ self.functions
139-
+ self.freestandingMacros
140-
+ self.attachedMacros
141-
}
142-
}
143-
extension Unidoc.Stats.Decl
121+
extension Unidoc.Stats.Decl:Unidoc.StatsCounters,
122+
BSONDocumentEncodable,
123+
BSONDocumentDecodable
144124
{
145125
@frozen public
146-
enum CodingKey:String, Sendable
126+
enum CodingKey:String, Sendable, CaseIterable
147127
{
148128
case typealiases = "T"
149129
case structures = "V"
@@ -161,52 +141,27 @@ extension Unidoc.Stats.Decl
161141
case freestandingMacros = "Y"
162142
case attachedMacros = "Z"
163143
}
164-
}
165-
extension Unidoc.Stats.Decl:BSONDocumentEncodable
166-
{
167-
public
168-
func encode(to bson:inout BSON.DocumentEncoder<CodingKey>)
169-
{
170-
bson[.typealiases] = self.typealiases != 0 ? self.typealiases : nil
171-
bson[.structures] = self.structures != 0 ? self.structures : nil
172-
bson[.protocols] = self.protocols != 0 ? self.protocols : nil
173-
bson[.classes] = self.classes != 0 ? self.classes : nil
174-
bson[.actors] = self.actors != 0 ? self.actors : nil
175-
bson[.requirements] = self.requirements != 0 ? self.requirements : nil
176-
bson[.witnesses] = self.witnesses != 0 ? self.witnesses : nil
177-
bson[.constructors] = self.constructors != 0 ? self.constructors : nil
178-
bson[.subscripts] = self.subscripts != 0 ? self.subscripts : nil
179-
bson[.functors] = self.functors != 0 ? self.functors : nil
180-
bson[.methods] = self.methods != 0 ? self.methods : nil
181-
bson[.operators] = self.operators != 0 ? self.operators : nil
182-
bson[.functions] = self.functions != 0 ? self.functions : nil
183144

184-
bson[.freestandingMacros] =
185-
self.freestandingMacros != 0 ? self.freestandingMacros : nil
186-
bson[.attachedMacros] =
187-
self.attachedMacros != 0 ? self.attachedMacros : nil
188-
}
189-
}
190-
extension Unidoc.Stats.Decl:BSONDocumentDecodable
191-
{
192-
@inlinable public
193-
init(bson:BSON.DocumentDecoder<CodingKey>) throws
145+
@inlinable public static
146+
subscript(key:CodingKey) -> WritableKeyPath<Self, Int>
194147
{
195-
self.init(
196-
typealiases: try bson[.typealiases]?.decode() ?? 0,
197-
structures: try bson[.structures]?.decode() ?? 0,
198-
protocols: try bson[.protocols]?.decode() ?? 0,
199-
classes: try bson[.classes]?.decode() ?? 0,
200-
actors: try bson[.actors]?.decode() ?? 0,
201-
requirements: try bson[.requirements]?.decode() ?? 0,
202-
witnesses: try bson[.witnesses]?.decode() ?? 0,
203-
constructors: try bson[.constructors]?.decode() ?? 0,
204-
subscripts: try bson[.subscripts]?.decode() ?? 0,
205-
functors: try bson[.functors]?.decode() ?? 0,
206-
methods: try bson[.methods]?.decode() ?? 0,
207-
operators: try bson[.operators]?.decode() ?? 0,
208-
functions: try bson[.functions]?.decode() ?? 0,
209-
freestandingMacros: try bson[.freestandingMacros]?.decode() ?? 0,
210-
attachedMacros: try bson[.attachedMacros]?.decode() ?? 0)
148+
switch key
149+
{
150+
case .typealiases: \.typealiases
151+
case .structures: \.structures
152+
case .protocols: \.protocols
153+
case .classes: \.classes
154+
case .actors: \.actors
155+
case .requirements: \.requirements
156+
case .witnesses: \.witnesses
157+
case .constructors: \.constructors
158+
case .subscripts: \.subscripts
159+
case .functors: \.functors
160+
case .methods: \.methods
161+
case .operators: \.operators
162+
case .functions: \.functions
163+
case .freestandingMacros: \.freestandingMacros
164+
case .attachedMacros: \.attachedMacros
165+
}
211166
}
212167
}

Sources/UnidocRecords/Volumes/Stats/Unidoc.Stats.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
import BSON
2+
import BSON_OrderedCollections
3+
import OrderedCollections
24

35
extension Unidoc
46
{
57
@frozen public
68
struct Stats:Equatable, Sendable
79
{
10+
/// External links, by domain.
11+
public
12+
var hyperlinks:OrderedDictionary<BSON.Key, Int>
13+
/// System programming interfaces.
14+
public
15+
var interfaces:OrderedDictionary<BSON.Key, Int>
16+
817
public
918
var coverage:Coverage
1019
public
1120
var decls:Decl
1221

1322
@inlinable public
14-
init(coverage:Coverage = .init(), decls:Decl = .init())
23+
init(
24+
hyperlinks:OrderedDictionary<BSON.Key, Int> = [:],
25+
interfaces:OrderedDictionary<BSON.Key, Int> = [:],
26+
coverage:Coverage = .init(),
27+
decls:Decl = .init())
1528
{
29+
self.hyperlinks = hyperlinks
30+
self.interfaces = interfaces
1631
self.coverage = coverage
1732
self.decls = decls
1833
}
@@ -23,6 +38,8 @@ extension Unidoc.Stats
2338
public
2439
enum CodingKey:String, Sendable
2540
{
41+
case hyperlinks = "H"
42+
case interfaces = "I"
2643
case coverage = "C"
2744
case decls = "D"
2845
}
@@ -32,6 +49,8 @@ extension Unidoc.Stats:BSONDocumentEncodable
3249
public
3350
func encode(to bson:inout BSON.DocumentEncoder<CodingKey>)
3451
{
52+
bson[.hyperlinks] = self.hyperlinks.isEmpty ? nil : self.hyperlinks
53+
bson[.interfaces] = self.interfaces.isEmpty ? nil : self.interfaces
3554
bson[.coverage] = self.coverage
3655
bson[.decls] = self.decls
3756
}
@@ -42,6 +61,8 @@ extension Unidoc.Stats:BSONDocumentDecodable
4261
init(bson:BSON.DocumentDecoder<CodingKey>) throws
4362
{
4463
self.init(
64+
hyperlinks: try bson[.hyperlinks]?.decode() ?? [:],
65+
interfaces: try bson[.interfaces]?.decode() ?? [:],
4566
coverage: try bson[.coverage].decode(),
4667
decls: try bson[.decls].decode())
4768
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import BSON
2+
3+
extension Unidoc
4+
{
5+
public
6+
typealias StatsCounters = _UnidocStatsCounters
7+
}
8+
public
9+
protocol _UnidocStatsCounters:ExpressibleByDictionaryLiteral where Key == Never
10+
{
11+
associatedtype CodingKey:RawRepresentable<String>, CaseIterable
12+
associatedtype Value
13+
14+
static
15+
subscript(key:CodingKey) -> WritableKeyPath<Self, Int> { get }
16+
}
17+
extension Unidoc.StatsCounters
18+
{
19+
@inlinable public
20+
var total:Int
21+
{
22+
CodingKey.allCases.reduce(0) { $0 + self[keyPath: Self[$1]] }
23+
}
24+
}
25+
extension Unidoc.StatsCounters where Self:BSONDocumentEncodable
26+
{
27+
@inlinable public
28+
func encode(to bson:inout BSON.DocumentEncoder<CodingKey>)
29+
{
30+
for key in CodingKey.allCases
31+
{
32+
let value:Int = self[keyPath: Self[key]]
33+
if value != 0
34+
{
35+
bson[key] = value
36+
}
37+
}
38+
}
39+
}
40+
extension Unidoc.StatsCounters where Self:BSONDocumentDecodable
41+
{
42+
@inlinable public
43+
init(bson:BSON.DocumentDecoder<CodingKey>) throws
44+
{
45+
self = [:]
46+
for field:BSON.FieldDecoder<CodingKey> in bson
47+
{
48+
self[keyPath: Self[field.key]] = try field.decode()
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)