xref: /aosp_15_r20/external/bazel-skylib/rules/directory/private/paths.bzl (revision bcb5dc7965af6ee42bf2f21341a2ec00233a8c8a)
1# Copyright 2024 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Skylib module containing path operations on directories."""
16
17load("//lib:paths.bzl", "paths")
18
19_NOT_FOUND = """{directory} does not contain an entry named {name}.
20Instead, it contains the following entries:
21{children}
22
23"""
24_WRONG_TYPE = "Expected {dir}/{name} to have type {want}, but got {got}"
25
26# These correspond to an "enum".
27FILE = "file"
28DIRECTORY = "directory"
29
30def _check_path_relative(path):
31    if paths.is_absolute(path):
32        fail("Path must be relative. Got {path}".format(path = path))
33
34def _get_direct_child(directory, name, require_type = None):
35    """Gets the direct child of a directory.
36
37    Args:
38        directory: (DirectoryInfo) The directory to look within.
39        name: (string) The name of the directory/file to look for.
40        require_type: (Optional[DIRECTORY|FILE]) If provided, must return
41          either a the corresponding type.
42
43    Returns:
44        (File|DirectoryInfo) The content contained within.
45    """
46    entry = directory.entries.get(name, None)
47    if entry == None:
48        fail(_NOT_FOUND.format(
49            directory = directory.human_readable,
50            name = repr(name),
51            children = "\n".join(directory.entries.keys()),
52        ))
53    if require_type == DIRECTORY and type(entry) == "File":
54        fail(_WRONG_TYPE.format(
55            dir = directory.human_readable,
56            name = name,
57            want = "Directory",
58            got = "File",
59        ))
60
61    if require_type == FILE and type(entry) != "File":
62        fail(_WRONG_TYPE.format(
63            dir = directory.human_readable,
64            name = name,
65            want = "File",
66            got = "Directory",
67        ))
68    return entry
69
70def get_path(directory, path, require_type = None):
71    """Gets a subdirectory or file contained within a directory.
72
73    Example: `get_path(directory, "a/b", require_type=FILE)`
74      -> the file  corresponding to `directory.path + "/a/b"`
75
76    Args:
77        directory: (DirectoryInfo) The directory to look within.
78        path: (string) The path of the directory to look for within it.
79        require_type: (Optional[DIRECTORY|FILE]) If provided, must return
80          either a the corresponding type.
81
82    Returns:
83        (File|DirectoryInfo) The directory contained within.
84    """
85    _check_path_relative(path)
86
87    chunks = path.split("/")
88    for dirname in chunks[:-1]:
89        directory = _get_direct_child(directory, dirname, require_type = DIRECTORY)
90    return _get_direct_child(
91        directory,
92        chunks[-1],
93        require_type = require_type,
94    )
95