blob: fe93d5d61c457c21941e212a51ee3bd9ceebbdd5 [file] [log] [blame]
Andrew Jefferyf4019fe2018-05-18 16:42:18 +09301#!/usr/bin/python3
2#
3# SPDX-License-Identifier: Apache-2.0
4# Copyright (C) 2018 IBM Corp.
5
6import argparse
7import sys
8from collections import namedtuple, OrderedDict
9from enum import Enum, unique
Patrick Williams9a014392021-06-21 14:57:29 -050010from typing import (
11 Dict,
12 NamedTuple,
13 Iterator,
14 Sequence,
15 Union,
16 Optional,
17 List,
18 cast,
19 IO,
20)
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093021from pprint import pprint
22
Patrick Williams9a014392021-06-21 14:57:29 -050023
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093024@unique
25class LineType(Enum):
26 REPO = 1
27 MAINTAINER = 2
28 REVIEWER = 3
29 FORKED = 4
30 COMMENT = 5
31
Patrick Williams9a014392021-06-21 14:57:29 -050032
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093033@unique
34class ParseState(Enum):
35 BEGIN = 1
36 BLOCK = 2
37
Patrick Williams9a014392021-06-21 14:57:29 -050038
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093039Email = NamedTuple("Email", [("name", str), ("address", str)])
40Identity = NamedTuple("Identity", [("email", Email), ("irc", Optional[str])])
41Entry = NamedTuple("Entry", [("type", LineType), ("content", str)])
42
Patrick Williams9a014392021-06-21 14:57:29 -050043
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093044def parse_line(line: str) -> Optional[Entry]:
45 sline = line.strip()
46 if not sline:
47 return None
48
49 if sline == "MAINTAINERS":
50 return Entry(LineType.REPO, sline)
51
52 tag = line[:2]
Patrick Williams9a014392021-06-21 14:57:29 -050053 if "@" in tag:
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093054 return Entry(LineType.REPO, sline[1:].split(":")[0].strip())
Patrick Williams9a014392021-06-21 14:57:29 -050055 elif tag == "M:":
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093056 return Entry(LineType.MAINTAINER, sline.split(":")[1].strip())
Patrick Williams9a014392021-06-21 14:57:29 -050057 elif tag == "R:":
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093058 return Entry(LineType.REVIEWER, sline.split(":")[1].strip())
Patrick Williams9a014392021-06-21 14:57:29 -050059 elif tag == "F:":
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093060 return Entry(LineType.FORKED, sline[2:].strip())
Patrick Williams9a014392021-06-21 14:57:29 -050061 elif "#" in tag:
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093062 return Entry(LineType.COMMENT, line)
63
64 return None
65
Patrick Williams9a014392021-06-21 14:57:29 -050066
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093067D = Union[str, List[Identity], List[str]]
68
Patrick Williams9a014392021-06-21 14:57:29 -050069
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093070def parse_repo(content: str) -> str:
71 return content
72
Patrick Williams9a014392021-06-21 14:57:29 -050073
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093074def parse_forked(content: str) -> str:
75 return content
76
Patrick Williams9a014392021-06-21 14:57:29 -050077
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093078def parse_irc(src: Iterator[str]) -> Optional[str]:
79 irc = ""
80 for c in src:
Patrick Williams9a014392021-06-21 14:57:29 -050081 if c == "#":
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093082 return None
Patrick Williams9a014392021-06-21 14:57:29 -050083 if c == "<":
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093084 break
85 else:
86 return None
87
88 for c in src:
Patrick Williams9a014392021-06-21 14:57:29 -050089 if c in "!#":
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093090 return irc.strip()
91 irc += c
92
93 raise ValueError("Unterminated IRC handle")
94
Patrick Williams9a014392021-06-21 14:57:29 -050095
Andrew Jefferyf4019fe2018-05-18 16:42:18 +093096def parse_address(src: Iterator[str]) -> str:
97 addr = ""
98 for c in src:
Patrick Williams9a014392021-06-21 14:57:29 -050099 if c in ">#":
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930100 return addr.strip()
101 addr += c
102 raise ValueError("Unterminated email address")
103
Patrick Williams9a014392021-06-21 14:57:29 -0500104
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930105def parse_name(src: Iterator[str]) -> str:
106 name = ""
107 for c in src:
Patrick Williams9a014392021-06-21 14:57:29 -0500108 if c in "<#":
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930109 return name.strip()
110 name += c
111 raise ValueError("Unterminated name")
112
Patrick Williams9a014392021-06-21 14:57:29 -0500113
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930114def parse_email(src: Iterator[str]) -> Email:
115 name = parse_name(src)
116 address = parse_address(src)
117 return Email(name, address)
118
Patrick Williams9a014392021-06-21 14:57:29 -0500119
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930120def parse_identity(content: str) -> Identity:
121 ci = iter(content)
122 email = parse_email(ci)
123 irc = parse_irc(ci)
124 return Identity(email, irc)
125
Patrick Williams9a014392021-06-21 14:57:29 -0500126
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930127B = Dict[LineType, D]
128
Patrick Williams9a014392021-06-21 14:57:29 -0500129
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930130def parse_block(src: Iterator[str]) -> Optional[B]:
131 state = ParseState.BEGIN
Andrew Jeffery382f2862018-05-23 17:05:50 +0930132 repo = cast(B, OrderedDict())
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930133 for line in src:
134 try:
135 entry = parse_line(line)
136 if state == ParseState.BEGIN and not entry:
137 continue
138 elif state == ParseState.BEGIN and entry:
139 state = ParseState.BLOCK
140 elif state == ParseState.BLOCK and not entry:
141 return repo
142
143 assert entry
144
145 if entry.type == LineType.REPO:
146 repo[entry.type] = parse_repo(entry.content)
Patrick Williams9a014392021-06-21 14:57:29 -0500147 elif entry.type in {LineType.MAINTAINER, LineType.REVIEWER}:
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930148 if not entry.type in repo:
149 repo[entry.type] = cast(List[Identity], list())
150 cast(list, repo[entry.type]).append(parse_identity(entry.content))
151 elif entry.type == LineType.FORKED:
152 repo[entry.type] = parse_forked(entry.content)
153 elif entry.type == LineType.COMMENT:
154 if not entry.type in repo:
155 repo[entry.type] = cast(List[str], list())
156 cast(list, repo[entry.type]).append(entry.content)
157 except ValueError as e:
158 print("Failed to parse line '{}': {}".format(line.strip(), e))
159
160 if not repo:
161 return None
162
163 return repo
164
Patrick Williams9a014392021-06-21 14:57:29 -0500165
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930166def trash_preamble(src: Iterator[str]) -> None:
167 s = 0
168 for line in src:
169 sline = line.strip()
170 if "START OF MAINTAINERS LIST" == sline:
171 s = 1
172 if s == 1 and sline == "-------------------------":
173 break
174
Patrick Williams9a014392021-06-21 14:57:29 -0500175
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930176def parse_maintainers(src: Iterator[str]) -> Dict[D, B]:
Andrew Jeffery382f2862018-05-23 17:05:50 +0930177 maintainers = cast(Dict[D, B], OrderedDict())
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930178 trash_preamble(src)
179 while True:
Andrew Jeffery382f2862018-05-23 17:05:50 +0930180 repo = cast(B, parse_block(src))
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930181 if not repo:
182 break
183 maintainers[repo[LineType.REPO]] = repo
184 return maintainers
185
Patrick Williams9a014392021-06-21 14:57:29 -0500186
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930187def assemble_name(name: str, dst: IO[str]) -> None:
188 dst.write(name)
189
Patrick Williams9a014392021-06-21 14:57:29 -0500190
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930191def assemble_address(address: str, dst: IO[str]) -> None:
192 dst.write("<")
193 dst.write(address)
194 dst.write(">")
195
Patrick Williams9a014392021-06-21 14:57:29 -0500196
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930197def assemble_email(email: Email, dst: IO[str]) -> None:
198 assemble_name(email.name, dst)
199 dst.write(" ")
200 assemble_address(email.address, dst)
201
Patrick Williams9a014392021-06-21 14:57:29 -0500202
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930203def assemble_irc(irc: Optional[str], dst: IO[str]) -> None:
204 if irc:
205 dst.write(" ")
206 dst.write("<")
207 dst.write(irc)
208 dst.write("!>")
209
Patrick Williams9a014392021-06-21 14:57:29 -0500210
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930211def assemble_identity(identity: Identity, dst: IO[str]) -> None:
212 assemble_email(identity.email, dst)
213 assemble_irc(identity.irc, dst)
214
Patrick Williams9a014392021-06-21 14:57:29 -0500215
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930216def assemble_maintainers(identities: List[Identity], dst: IO[str]) -> None:
217 for i in identities:
218 dst.write("M: ")
219 assemble_identity(i, dst)
220 dst.write("\n")
221
Patrick Williams9a014392021-06-21 14:57:29 -0500222
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930223def assemble_reviewers(identities: List[Identity], dst: IO[str]) -> None:
224 for i in identities:
225 dst.write("R: ")
226 assemble_identity(i, dst)
227 dst.write("\n")
228
Patrick Williams9a014392021-06-21 14:57:29 -0500229
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930230def assemble_forked(content: str, dst: IO[str]) -> None:
231 if content:
232 dst.write("F: ")
233 dst.write(content)
234 dst.write("\n")
235
Patrick Williams9a014392021-06-21 14:57:29 -0500236
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930237def assemble_comment(content: List[str], dst: IO[str]) -> None:
238 dst.write("".join(content))
239
Patrick Williams9a014392021-06-21 14:57:29 -0500240
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930241def assemble_block(block: B, default: B, dst: IO[str]) -> None:
242 if LineType.COMMENT in block:
243 assemble_comment(cast(List[str], block[LineType.COMMENT]), dst)
244 if LineType.MAINTAINER in block:
245 maintainers = block[LineType.MAINTAINER]
246 else:
247 maintainers = default[LineType.MAINTAINER]
248 assemble_maintainers(cast(List[Identity], maintainers), dst)
249 if LineType.REVIEWER in block:
250 assemble_reviewers(cast(List[Identity], block[LineType.REVIEWER]), dst)
251 if LineType.FORKED in block:
252 assemble_forked(cast(str, block[LineType.FORKED]), dst)
253
Patrick Williams9a014392021-06-21 14:57:29 -0500254
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930255def main() -> None:
256 parser = argparse.ArgumentParser()
Patrick Williams9a014392021-06-21 14:57:29 -0500257 parser.add_argument("maintainers", type=argparse.FileType("r"), default=sys.stdin)
258 parser.add_argument("output", type=argparse.FileType("w"), default=sys.stdout)
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930259 args = parser.parse_args()
260 blocks = parse_maintainers(args.maintainers)
261 for block in blocks.values():
262 print(block[LineType.REPO])
Patrick Williams9a014392021-06-21 14:57:29 -0500263 assemble_block(block, blocks["MAINTAINERS"], args.output)
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930264 print()
265
Patrick Williams9a014392021-06-21 14:57:29 -0500266
Andrew Jefferyf4019fe2018-05-18 16:42:18 +0930267if __name__ == "__main__":
268 main()