diff --git a/craft_parts/utils/partition_utils.py b/craft_parts/utils/partition_utils.py new file mode 100644 index 000000000..b60500209 --- /dev/null +++ b/craft_parts/utils/partition_utils.py @@ -0,0 +1,67 @@ +# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- +# +# Copyright 2023 Canonical Ltd. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License version 3 as published by the Free Software Foundation. +# +# 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +"""Partition helpers.""" + +import re +from pathlib import Path +from typing import Union + +from craft_parts.features import Features + + +# XXX: for context, you can see where this function in the prototype: +# https://github.com/mr-cal/craft-parts/commit/e2d15f1bb1da3207463ad3015b68509537b007c3 + + +def get_partition_compatible_filepath(filepath: Union[Path, str]) -> Union[Path, str]: + """Get a filepath compatible with the partitions feature. + + If the filepath begins with a partition, then the parentheses are stripped from the + the partition. For example, `(default)/file` is converted to `default/file`. + + If the filepath does not begin with a partition, the `default` partition is + prepended. For example, `file` is converted to `default/file`. + + :param filepath: The filepath to modify. + + :returns: A filepath that is compatible with the partitions feature. + """ + if not Features().enable_partitions: + return filepath + + # XXX: I think this is the correct thing to do. The default globs should not be + # modified (which comes from craft_parts/parts.py lines 55-58) + if filepath == "*": + return filepath + + # XXX: I know there is at least one problem with this regex: `(default)` and + # `(default)/` are treated differently + match = re.match("^\\((?P[a-z]+)\\)/(?P.*)", str(filepath)) + if match: + partition = match.group("partition") + everything_else = match.group("filepath") + else: + partition = "default" + everything_else = filepath + + new_filepath = Path(partition, everything_else) + + # XXX: craft-parts calls this function in different places. Sometimes the filepath + # is a string and something it is a Path object. I decided to make this function + # handle both scenarios but I wonder if this approach will create + # type-checking issues in the future. + return str(new_filepath) if isinstance(filepath, str) else new_filepath