diff --git a/osbuild/util/parsing.py b/osbuild/util/parsing.py new file mode 100644 index 00000000..303fbefa --- /dev/null +++ b/osbuild/util/parsing.py @@ -0,0 +1,33 @@ +"""Helpers related to parsing""" + +import re +from typing import Union + + +def parse_size(s: str) -> Union[int, str]: + """Parse a size string into a number or 'unlimited'. + + Supported suffixes: kB, kiB, MB, MiB, GB, GiB, TB, TiB + """ + units = [ + (r'^\s*(\d+)\s*kB$', 1000, 1), + (r'^\s*(\d+)\s*KiB$', 1024, 1), + (r'^\s*(\d+)\s*MB$', 1000, 2), + (r'^\s*(\d+)\s*MiB$', 1024, 2), + (r'^\s*(\d+)\s*GB$', 1000, 3), + (r'^\s*(\d+)\s*GiB$', 1024, 3), + (r'^\s*(\d+)\s*TB$', 1000, 4), + (r'^\s*(\d+)\s*TiB$', 1024, 4), + (r'^\s*(\d+)$', 1, 1), + (r'^unlimited$', "unlimited", 1), + ] + + for pat, base, power in units: + m = re.fullmatch(pat, s) + if m: + if isinstance(base, int): + return int(m.group(1)) * base ** power + if base == "unlimited": + return "unlimited" + + raise TypeError(f"invalid size value: '{s}'") diff --git a/test/mod/test_util_parsing.py b/test/mod/test_util_parsing.py new file mode 100644 index 00000000..faaa31a5 --- /dev/null +++ b/test/mod/test_util_parsing.py @@ -0,0 +1,39 @@ +"""Unit tests for osbuild.util.parsing""" + +import pytest + +from osbuild.util import parsing + + +def test_parse_size(): + cases = [ + ("123", True, 123), + ("123 kB", True, 123000), + ("123 KiB", True, 123 * 1024), + ("123 MB", True, 123 * 1000 * 1000), + ("123 MiB", True, 123 * 1024 * 1024), + ("123 GB", True, 123 * 1000 * 1000 * 1000), + ("123 GiB", True, 123 * 1024 * 1024 * 1024), + ("123 TB", True, 123 * 1000 * 1000 * 1000 * 1000), + ("123 TiB", True, 123 * 1024 * 1024 * 1024 * 1024), + ("123kB", True, 123000), + ("123KiB", True, 123 * 1024), + (" 123", True, 123), + (" 123kB", True, 123000), + (" 123KiB", True, 123 * 1024), + ("unlimited", True, "unlimited"), + ("string", False, 0), + ("123 KB", False, 0), + ("123 mb", False, 0), + ("123 PB", False, 0), + ("123 PiB", False, 0), + + ] + + for s, success, num in cases: + if not success: + with pytest.raises(TypeError): + parsing.parse_size(s) + else: + res = parsing.parse_size(s) + assert res == num, f"{s} parsed as {res} (wanted {num})"