Skip to content

Commit e885084

Browse files
committed
Added: Errors, buffer output, tests
1 parent 2311f65 commit e885084

File tree

6 files changed

+188
-93
lines changed

6 files changed

+188
-93
lines changed

Sources/SwiftSource/SwiftSource.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,8 @@ public struct SwiftSource: Decodable {
4343
let source = try String(contentsOf: fileURL)
4444
self.init(url: fileURL, source: source)
4545
}
46+
47+
public func declarations(level: SwiftAccessLevel) -> [SwiftDeclaration] {
48+
declarations.filter { $0.accessLevel.rawValue <= level.rawValue }
49+
}
4650
}

Sources/swift-doc-coverage/Output.swift

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,36 +23,49 @@
2323

2424
import Foundation
2525

26+
2627
class Output {
27-
let stream: UnsafeMutablePointer<FILE>
28+
var stream: UnsafeMutablePointer<FILE>? = nil
29+
var buffer: String = ""
30+
31+
init() {
32+
}
2833

2934
init(stream: UnsafeMutablePointer<FILE>) {
3035
self.stream = stream
3136
}
3237

3338
func write(_ text: String, terminator: String = "\n") {
34-
fputs(text, stream)
35-
fputs(terminator, stream)
39+
if let stream {
40+
fputs(text, stream)
41+
fputs(terminator, stream)
42+
}
43+
else {
44+
self.buffer.append(text)
45+
self.buffer.append(terminator)
46+
}
3647
}
3748
}
3849

3950
class TerminalOutput: Output {
4051

41-
init() {
52+
override init() {
4253
super.init(stream: Darwin.stdout)
4354
}
4455
}
4556

4657
class FileOutput: Output {
4758

48-
init(path: String) throws {
59+
init?(path: String) throws {
4960
let dirPath = NSString(string: path).deletingLastPathComponent
5061
if FileManager.default.fileExists(atPath: dirPath) == false {
5162
try FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true)
5263
}
5364

54-
guard let file = fopen(path.cString(using: .utf8), "w".cString(using: .utf8)) else {
55-
throw "Can't open file."
65+
let fileName = path.cString(using: .utf8)
66+
let mode = "w".cString(using: .utf8)
67+
guard let file = fopen(fileName, mode) else {
68+
return nil
5669
}
5770
super.init(stream: file)
5871
}

Sources/swift-doc-coverage/main.swift

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,19 @@ import ArgumentParser
2626
import SwiftSource
2727

2828

29-
extension String : LocalizedError {
30-
public var errorDescription: String? { self }
29+
enum Errors: String {
30+
case cantOpenFile = "Can't open file."
31+
case filesNotFound = "Swift files not found."
32+
case declarationsNotFound = "Swift declarations not found."
33+
case pathNotFound = "Path not found."
34+
case notSwiftFile = "Not swift file."
3135
}
3236

37+
extension Errors: LocalizedError {
38+
public var errorDescription: String? { self.rawValue }
39+
}
40+
41+
3342
enum AccessLevel: String, ExpressibleByArgument {
3443
case open, `public`, `internal`, `fileprivate`, `private`
3544

@@ -75,24 +84,39 @@ struct SwiftDocCoverage: ParsableCommand {
7584
@Option(name: .shortAndLong, help: "The file path for generated report.")
7685
var output: String?
7786

87+
private enum CodingKeys: String, CodingKey {
88+
case inputs, skipsHiddenFiles, ignoreFilenameRegex, minimumAccessLevel, report, output
89+
}
90+
7891
var sources: [SwiftSource] = []
92+
var out: Output? = nil
7993

8094
mutating func run() throws {
81-
let out: Output = output != nil
82-
? try FileOutput(path: output!)
83-
: TerminalOutput()
95+
// Output
96+
if out == nil {
97+
if let output = output {
98+
if let file = try FileOutput(path: output) {
99+
out = file
100+
}
101+
else {
102+
throw Errors.cantOpenFile
103+
}
104+
}
105+
else {
106+
out = TerminalOutput()
107+
}
108+
}
84109

85110
let urls = try inputs.flatMap {
86111
try Self.files(path: $0, ext: ".swift", skipsHiddenFiles: skipsHiddenFiles, ignoreFilenameRegex: ignoreFilenameRegex)
87112
}
88113

89114
guard urls.count > 0 else {
90-
throw "Swift files not found."
115+
throw Errors.filesNotFound
91116
}
92117

93118
let totalTime = Date()
94119
var i = 0
95-
let minAccessLevel = minimumAccessLevel.accessLevel.rawValue
96120

97121
// Sources
98122
sources = try urls.map { url in
@@ -101,7 +125,7 @@ struct SwiftDocCoverage: ParsableCommand {
101125
let source = try SwiftSource(fileURL: url)
102126

103127
if report != .json {
104-
let declarations = source.declarations.filter { $0.accessLevel.rawValue <= minAccessLevel }
128+
let declarations = source.declarations(level: minimumAccessLevel.accessLevel)
105129
if declarations.count > 0 {
106130
i += 1
107131
let filePath = url.absoluteString
@@ -118,9 +142,9 @@ struct SwiftDocCoverage: ParsableCommand {
118142
}
119143

120144
if report == .coverage {
121-
let declarations = sources.flatMap { $0.declarations.filter { $0.accessLevel.rawValue <= minAccessLevel } }
145+
let declarations = sources.flatMap { $0.declarations(level: minimumAccessLevel.accessLevel) }
122146
guard declarations.count > 0 else {
123-
throw "Swift declarations not found."
147+
throw Errors.declarationsNotFound
124148
}
125149

126150
let undocumented = declarations.filter { $0.isDocumented == false }
@@ -129,7 +153,7 @@ struct SwiftDocCoverage: ParsableCommand {
129153
let documentedCount = totalCount - undocumented.count
130154
let coverage = documentedCount * 100 / totalCount
131155

132-
out.write("\nTotal: \(coverage)% [\(documentedCount)/\(totalCount)] (\(Self.string(from: -totalTime.timeIntervalSinceNow)))")
156+
out?.write("\nTotal: \(coverage)% [\(documentedCount)/\(totalCount)] (\(Self.string(from: -totalTime.timeIntervalSinceNow)))")
133157
}
134158
else if report == .json {
135159
print(sources)
@@ -143,16 +167,17 @@ struct SwiftDocCoverage: ParsableCommand {
143167
}
144168
}
145169

146-
static func run(_ arguments: [String]? = nil) throws -> Self {
170+
static func run(output: Output? = nil, _ arguments: String...) throws -> Self {
147171
var cmd = try Self.parse(arguments)
172+
cmd.out = output
148173
try cmd.run()
149174
return cmd
150175
}
151176

152177
static func files(path: String, ext: String, skipsHiddenFiles: Bool, ignoreFilenameRegex: String) throws -> [URL] {
153178
var isDirectory: ObjCBool = false
154179
guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) else {
155-
throw "Path not found."
180+
throw Errors.pathNotFound
156181
}
157182

158183
if isDirectory.boolValue {
@@ -178,7 +203,7 @@ struct SwiftDocCoverage: ParsableCommand {
178203
// Skip by regex
179204
if let regex = regex {
180205
let fileName = fileURL.lastPathComponent
181-
let range = NSRange(location: 0, length: fileName.utf16.count)
206+
let range = NSRange(fileName.startIndex..., in: fileName)
182207
if regex.firstMatch(in: fileName, range: range) != nil {
183208
continue
184209
}
@@ -191,7 +216,7 @@ struct SwiftDocCoverage: ParsableCommand {
191216
}
192217

193218
guard path.hasSuffix(ext) else {
194-
throw "Not swift file."
219+
throw Errors.notSwiftFile
195220
}
196221
let url = URL(fileURLWithPath: path)
197222
return [url]
@@ -222,7 +247,7 @@ struct SwiftDocCoverage: ParsableCommand {
222247
return time
223248
}
224249

225-
static func coverage(i: Int, time: Date, filePath: String, declarations: [SwiftDeclaration], out: Output) {
250+
static func coverage(i: Int, time: Date, filePath: String, declarations: [SwiftDeclaration], out: Output?) {
226251
assert(declarations.count > 0)
227252

228253
let undocumented = declarations.filter { $0.isDocumented == false }
@@ -231,26 +256,26 @@ struct SwiftDocCoverage: ParsableCommand {
231256
let documentedCount = totalCount - undocumented.count
232257
let coverage = documentedCount * 100 / totalCount
233258

234-
out.write("\(i)) \(filePath): \(coverage)% [\(documentedCount)/\(totalCount)] (\(string(from: -time.timeIntervalSinceNow)))")
259+
out?.write("\(i)) \(filePath): \(coverage)% [\(documentedCount)/\(totalCount)] (\(string(from: -time.timeIntervalSinceNow)))")
235260

236261
if undocumented.count > 0 {
237262
let fileName = NSString(string: filePath).lastPathComponent
238263

239-
out.write("Undocumented:")
264+
out?.write("Undocumented:")
240265
undocumented.forEach {
241-
out.write("<\(fileName):\($0.line):\($0.column)> \($0.name)")
266+
out?.write("<\(fileName):\($0.line):\($0.column)> \($0.name)")
242267
}
243-
out.write("\n", terminator: "")
268+
out?.write("\n", terminator: "")
244269
}
245270
}
246271

247-
static func warnings(filePath: String, declarations: [SwiftDeclaration], out: Output) {
272+
static func warnings(filePath: String, declarations: [SwiftDeclaration], out: Output?) {
248273
assert(declarations.count > 0)
249274

250275
declarations
251276
.filter { $0.isDocumented == false }
252277
.forEach {
253-
out.write("\(filePath):\($0.line):\($0.column): warning: No documentation for '\($0.name)'.")
278+
out?.write("\(filePath):\($0.line):\($0.column): warning: No documentation for '\($0.name)'.")
254279
}
255280
}
256281
}

Tests/SwiftDocCoverageTests/Resources/Rect/AlternativeRect.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Foundation
22

33
struct AlternativeRect {
4-
var origin = Point()
5-
var size = Size()
4+
fileprivate var origin = Point()
5+
private var size = Size()
66
var center: Point {
77
get {
88
let centerX = origin.x + (size.width / 2)

Tests/SwiftDocCoverageTests/Resources/Rect/CompactRect.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Foundation
22

3+
/// Doc
34
struct CompactRect {
5+
/** Doc */
46
var origin = Point()
57
var size = Size()
68
var center: Point {

0 commit comments

Comments
 (0)