1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright (c) 2022 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import os
17import sys
18import re
19import argparse
20import subprocess
21import logging
22
23
24def add2dict(diff_dict: dict, path: str, line_num: str, content):
25    key = path
26    value = [line_num, content]
27
28    if key in diff_dict:
29        value_list = diff_dict.pop(key)
30        value_list.append(value)
31    else:
32        value_list = [value]
33
34    diff_dict.update({key: value_list})
35
36
37def __diff_match_file_start(control_block: dict, line: str):
38    pattern = "diff --git"
39    if not line.startswith(pattern):
40        return False
41
42    control_block["line_num"] = 0
43    control_block["fullpath"] = ""
44    control_block["match_flag"] = False
45    control_block["curr_key"] = ""
46    control_block["is_new_file"] = False
47
48    return True
49
50
51def __diff_match_is_newfile(control_block: dict, line: str):
52    pattern = "new file"
53    if not line.startswith(pattern):
54        return False
55
56    control_block["is_new_file"] = True
57    return True
58
59
60def __diff_match_filename_with_minus(control_block: dict, line: str):
61    pattern = "---\ (a/)?.*"
62    if re.match(pattern, line) is None:
63        return False
64
65    control_block["match_flag"] = False
66    return True
67
68
69def __diff_match_filename_with_plus(control_block: dict, line: str):
70    pattern = "\+\+\+\ b/(.*)"
71    if re.match(pattern, line) is None:
72        return False
73
74    for key in control_block["diff_dict"]:
75        if re.search(key, line) is not None:
76            control_block["curr_key"] = key
77            res = re.match(pattern, line)
78            control_block["fullpath"] = (
79                "{}, {}".format(control_block["pull_request_url"], res.group(1).strip())
80            )
81            if control_block['is_new_file'] is True:
82                control_block["fullpath"] = "%s%s" % (
83                    control_block["fullpath"], "(new file)")
84            control_block["match_flag"] = True
85    return True
86
87
88def __diff_match_start_linenum(control_block: dict, line: str):
89    pattern = "@@\ -[0-9]+,[0-9]+\ \+([0-9]+)(,[0-9]+)?\ @@.*"
90    if control_block["match_flag"] is False or re.match(pattern, line) is None:
91        return False
92
93    res = re.match(pattern, line)
94    control_block["line_num"] = int(res.group(1))
95    return True
96
97
98def __diff_match_code_line(control_block: dict, line: str):
99    diff_dict = control_block["diff_dict"]
100    pattern1 = "[\ +-](.*)"
101    pattern2 = "([\ +-])?(.*)"
102    if control_block["match_flag"] is False or re.match(pattern1, line) is None:
103        return False
104
105    res = re.match(pattern2, line)
106    if res.group(1) == "+":
107        add2dict(
108            diff_dict[control_block["curr_key"]],
109            control_block["fullpath"],
110            control_block["line_num"],
111            res.group(2),
112        )
113    if res.group(1) != "-":
114        control_block["line_num"] = control_block["line_num"] + 1
115    return True
116
117
118def strip_diff(diff_dict: dict, pull_request_url: str, gitee_pr_diff: str):
119    control_block = {
120        "line_num": 0,
121        "fullpath": "",
122        "match_flag": False,
123        "curr_key": "",
124        "diff_dict": diff_dict,
125        "pull_request_url": pull_request_url,
126        "is_new_file": False,
127    }
128
129    strip_diff_handlers = [
130        __diff_match_file_start,
131        __diff_match_is_newfile,
132        __diff_match_filename_with_minus,
133        __diff_match_filename_with_plus,
134        __diff_match_start_linenum,
135        __diff_match_code_line,
136    ]
137
138    for line in gitee_pr_diff.splitlines():
139        for handler in strip_diff_handlers:
140            if handler(control_block, line) is True:
141                break
142
143
144def get_diff_by_repo_pr_num(repo_url: str, pr_num: int):
145    diff_url = "%spulls/%s.diff" % (repo_url, str(pr_num))
146    cmd = "curl -L -s " + diff_url
147    gitee_pr_diff = ""
148    try:
149        ret = subprocess.Popen(
150            ["/usr/bin/curl", "-L", "-s", diff_url],
151            stdout=subprocess.PIPE,
152            stderr=subprocess.PIPE,
153            errors="replace",
154        )
155        gitee_pr_diff, errorout = ret.communicate()
156        if len(errorout) != 0:
157            logging.error("Popen error: ", errorout)
158    except Exception as err:
159        logging.error("error %s", err, cmd)
160
161    return gitee_pr_diff
162
163
164class GiteeCsctPrehandler:
165    def __init__(self, pr_list: str, *patterns):
166        self.diff_dict = {}
167        for pattern in patterns:
168            pattern_dict = {pattern: {}}
169            self.diff_dict.update(pattern_dict)
170
171        repo_pr_num_list = pr_list.split(";")
172        for pr_item in repo_pr_num_list:
173            pr_split_group = pr_item.split("pulls/")
174            repo_url = pr_split_group[0].strip()
175            pr_num = pr_split_group[1].strip("/")
176
177            gitee_pr_diff = get_diff_by_repo_pr_num(repo_url, pr_num)
178            strip_diff(self.diff_dict, pr_item, gitee_pr_diff)
179
180    def clear_repo_num_file(self):
181        self.diff_dict.clear()
182
183    def get_diff_dict(self, pattern):
184        ret_diff = {}
185        if pattern in self.diff_dict.keys():
186            ret_diff = self.diff_dict[pattern]
187        return ret_diff
188
189
190def test():
191    if len(sys.argv) == 1:
192        sys.stderr.write("test error: pr_list is empty.\n")
193        return
194
195    pr_list = sys.argv[1]
196    csct_prehandler = GiteeCsctPrehandler(
197        pr_list, "BUILD.gn", "bundle.json", ".gni")
198
199    print("==================start get diff====================")
200    print(csct_prehandler.get_diff_dict("BUILD.gn"))
201    print("==================start get diff====================")
202    print(csct_prehandler.get_diff_dict("bundle.json"))
203    print("========================end=========================")
204
205
206if __name__ == "__main__":
207    sys.exit(test())
208