"""Helpers related to parsing""" import os import re from typing import Dict, Tuple, Union from urllib.parse import ParseResult, urlparse 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}'") def find_mount_root(url: ParseResult, args: Dict) -> os.PathLike: """ Parses the mount URL to extract the root path. Parameters: - url (ParseResult): The ParseResult object obtained from urlparse. - args (Dict):A dictionary containing arguments including mounts and path information as passed by osbuild.api.arguments() """ name = url.netloc if name: root = args["mounts"].get(name, {}).get("path") if root is None: raise ValueError(f"Unknown mount '{name}'") else: root = args["paths"]["mounts"] return root def parse_input(url: ParseResult, args: Dict) -> os.PathLike: """ Parses the input URL to extract the root path. Parameters: - url (ParseResult): The ParseResult object obtained from urlparse. - args (Dict): A dictionary containing arguments including mounts and path information as passed by osbuild.api.arguments() """ name = url.netloc root = args["inputs"].get(name, {}).get("path") if root is None: raise ValueError(f"Unknown input '{name}'") return root def parse_location_into_parts(location: str, args: Dict) -> Tuple[str, str]: """ Parses the location URL to derive the corresponding root and url path. Parameters: - location (str): The location URL to be parsed. If the URL has no scheme, then 'tree://' is implied - args (Dict): A dictionary containing arguments including mounts and path information as passed by osbuild.api.arguments() """ if "://" not in location: location = f"tree://{location}" url = urlparse(location) scheme = url.scheme if scheme == "tree": root = args["tree"] elif scheme == "mount": root = find_mount_root(url, args) elif scheme == "input": root = parse_input(url, args) else: raise ValueError(f"Unsupported scheme '{scheme}'") if not url.path.startswith("/"): raise ValueError(f"url.path from location must start with '/', got: {url.path}") return root, url.path def parse_location(location: str, args: Dict) -> str: """ Parses the location URL to derive the corresponding file path. Parameters: - location (str): The location URL to be parsed. - args (Dict): A dictionary containing arguments including mounts and path information as passed by osbuild.api.arguments() """ root, urlpath = parse_location_into_parts(location, args) path = os.path.relpath(urlpath, "/") path = os.path.join(root, path) path = os.path.normpath(path) if urlpath.endswith("/"): path = os.path.join(path, ".") return path