# SPDX-License-Identifier: AGPL-3.0-or-later # Building Hydrilla packages. # # This file is part of Hydrilla # # Copyright (C) 2021, 2022 Wojtek Kosior # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # # # I, Wojtek Kosior, thereby promise not to sue for violation of this # file's license. Although I request that you do not make use this code # in a proprietary program, I am not going to enforce this in court. import re as _re import json as _json from typing import Optional as _Optional _strip_comment_re = _re.compile(r''' ^ # match from the beginning of each line ( # catch the part before '//' comment (?: # this group matches either a string or a single out-of-string character [^"/] | " (?: # this group matches any in-a-string character [^"\\] | # match any normal character \\[^u] | # match any escaped character like '\f' or '\n' \\u[a-fA-F0-9]{4} # match an escape )* " )* ) # expect either end-of-line or a comment: # * unterminated strings will cause matching to fail # * bad comment (with '/' instead of '//') will be indicated by second group # having length 1 instead of 2 or 0 (//?|$) ''', _re.VERBOSE) def strip_json_comments(text: str) -> str: """ Accept JSON text with optional C++-style ('//') comments and return the text with comments removed. Consecutive slashes inside strings are handled properly. A spurious single slash ('/') shall generate an error. Errors in JSON itself shall be ignored. """ processed = 0 stripped_text = [] for line in text.split('\n'): match = _strip_comment_re.match(line) if match is None: # unterminated string # ignore this error, let json module report it stripped = line elif len(match[2]) == 1: raise _json.JSONDecodeError('bad comment', text, processed + len(match[1])) else: stripped = match[1] stripped_text.append(stripped) processed += len(line) + 1 return '\n'.join(stripped_text) def normalize_version(ver: list[int]) -> list[int]: """Strip right-most zeroes from 'ver'. The original list is not modified.""" new_len = 0 for i, num in enumerate(ver): if num != 0: new_len = i + 1 return ver[:new_len] def parse_version(ver_str: str) -> list[int]: """ Convert 'ver_str' into an array representation, e.g. for ver_str="4.6.13.0" return [4, 6, 13, 0]. """ return [int(num) for num in ver_str.split('.')] def version_string(ver: list[int], rev: _Optional[int]=None) -> str: """ Produce version's string representation (optionally with revision), like: 1.2.3-5 No version normalization is performed. """ return '.'.join([str(n) for n in ver]) + ('' if rev is None else f'-{rev}')