-
Notifications
You must be signed in to change notification settings - Fork 11
/
GitLogMiddleware.swift
98 lines (86 loc) · 2.75 KB
/
GitLogMiddleware.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
// Noze.io Simple Connect based WebServer
import core
import streams
import http
import connect
import console
import child_process
// This is a demo on how to transform stuff in a streaming
// way along the piping queue.
//
// What it does:
// - we call `git log` and pipe the stdout
// - into `readlines`, which transforms the bytes into [String] lines
// - those are piped into the `linesToRecords` function, which
// produces `GitLogEntry` structs, which are then piped into
// - the `recordsToHTML` function, which produces HTML bytes
// - which are finally piped into the response
// Lots of piping going on.
struct GitLogEntry {
let commit : String
let author : String
let email : String
let date : String
}
extension String { // add stuff missing in String w/o Foundation
func componentsSeparated(by c: Character) -> [ String ] {
#if swift(>=3.2)
return split(separator: c).map { String($0) }
#else
return characters.split(separator: c).map { String($0) }
#endif
}
}
func linesToRecords(chunk : [String]?,
push : ( [GitLogEntry]? ) -> Void,
end : ( Error?, [GitLogEntry]? ) -> Void)
{
guard let lines = chunk else { end(nil,nil); return }
let records : [ GitLogEntry ] = lines.map { line in
let fields = line.componentsSeparated(by: "|")
guard fields.count > 3 else {
print("SHORT LINE: \(line) \(fields)")
return GitLogEntry(commit:line, author:"", email:"", date:"")
}
return GitLogEntry(
commit: fields[0],
author: fields[1],
email: fields[2],
date: fields[3]
)
}
end(nil, records)
}
func recordsToHTML(chunk : [ GitLogEntry ]?,
push : ( [UInt8]? ) -> Void,
end : ( Error?, [UInt8]? ) -> Void)
{
guard let records = chunk else { end(nil,nil); return }
for r in records {
let s =
"<tr><td>\(r.date)</td><td>\(r.author)</td><td>\(r.commit)</td></tr>"
push(Array<UInt8>(s.utf8)) // `res` expects bytes
}
end(nil, nil)
}
/// This is the actual middleware function
func gitLog(req: IncomingMessage, res: ServerResponse, next: (Any...) -> Void) {
res.write("<h3>git log</h3>")
res.write("<table border='1'>")
res.write("<tr><th>Date</th><th>Author</th><th>Commit</th></tr>")
let s = spawn("git", "log", "-100", "--pretty=format:%H|%an|<%ae>|%ad")
| readlines
| through2(linesToRecords)
| through2(recordsToHTML)
// Swift3 2016-05-09 falls apart on this, maybe this is due to
// operator ordering (| vs .)?
_ = s.pipe(res, endOnFinish: false)
.onError { error in
print("Pipe error: \(error)")
}
.onUnpipe { _ in
// TODO: sometimes the socket is closed before this happens?
res.write("</table>")
res.end()
}
}