Blender V4.3
asset_catalog_path.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BLI_path_utils.hh"
12
13#include <sstream>
14
15namespace blender::asset_system {
16
17const char AssetCatalogPath::SEPARATOR = '/';
18
19AssetCatalogPath::AssetCatalogPath(std::string path) : path_(std::move(path)) {}
20
22
23AssetCatalogPath::AssetCatalogPath(const char *path) : path_(path) {}
24
26 : path_(std::move(other_path.path_))
27{
28}
29
31{
32 std::hash<std::string> hasher{};
33 return hasher(path_);
34}
35
37{
38 return path_.length();
39}
40
41const char *AssetCatalogPath::c_str() const
42{
43 return path_.c_str();
44}
45
46const std::string &AssetCatalogPath::str() const
47{
48 return path_;
49}
50
52{
53 const size_t last_sep_index = path_.rfind(SEPARATOR);
54 if (last_sep_index == std::string::npos) {
55 return StringRefNull(path_);
56 }
57
58 return StringRefNull(path_.c_str() + last_sep_index + 1);
59}
60
62{
63 return path_ == other_path.path_;
64}
65
67{
68 return !(*this == other_path);
69}
70
71bool AssetCatalogPath::operator<(const AssetCatalogPath &other_path) const
72{
73 return path_ < other_path.path_;
74}
75
77{
78 /* `"" / "path"` or `"path" / ""` should just result in `"path"` */
79 if (!*this) {
80 return path_to_append;
81 }
82 if (!path_to_append) {
83 return *this;
84 }
85
86 std::stringstream new_path;
87 new_path << path_ << SEPARATOR << path_to_append.path_;
88 return AssetCatalogPath(new_path.str());
89}
90
91AssetCatalogPath::operator bool() const
92{
93 return !path_.empty();
94}
95
96std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append)
97{
98 stream << path_to_append.path_;
99 return stream;
100}
101
103{
104 std::stringstream clean_components;
105 bool first_component_seen = false;
106
107 this->iterate_components([&clean_components, &first_component_seen](StringRef component_name,
108 bool /*is_last_component*/) {
109 const std::string clean_component = cleanup_component(component_name);
110
111 if (clean_component.empty()) {
112 /* These are caused by leading, trailing, or double slashes. */
113 return;
114 }
115
116 /* If a previous path component has been streamed already, we need a path separator. This
117 * cannot use the `is_last_component` boolean, because the last component might be skipped due
118 * to the condition above. */
119 if (first_component_seen) {
120 clean_components << SEPARATOR;
121 }
122 first_component_seen = true;
123
124 clean_components << clean_component;
125 });
126
127 return AssetCatalogPath(clean_components.str());
128}
129
131{
132 std::string cleaned = component_name.trim();
133 /* Replace colons with something else, as those are used in the CDF file as delimiter. */
134 std::replace(cleaned.begin(), cleaned.end(), ':', '-');
135 return cleaned;
136}
137
139{
140 if (!other_path) {
141 /* The empty path contains all other paths. */
142 return true;
143 }
144
145 if (path_ == other_path.path_) {
146 /* Weak is-in relation: equal paths contain each other. */
147 return true;
148 }
149
150 /* To be a child path of 'other_path', our path must be at least a separator and another
151 * character longer. */
152 if (this->length() < other_path.length() + 2) {
153 return false;
154 }
155
156 /* Create StringRef to be able to use .startswith(). */
157 const StringRef this_path(path_);
158 const bool prefix_ok = this_path.startswith(other_path.path_);
159 const char next_char = this_path[other_path.length()];
160 return prefix_ok && next_char == SEPARATOR;
161}
162
164{
165 if (!*this) {
166 return AssetCatalogPath("");
167 }
168 std::string::size_type last_sep_index = path_.rfind(SEPARATOR);
169 if (last_sep_index == std::string::npos) {
170 return AssetCatalogPath("");
171 }
172 return AssetCatalogPath(path_.substr(0, last_sep_index));
173}
174
176{
177 const char *next_slash_ptr;
178
179 for (const char *path_component = path_.data(); path_component && path_component[0];
180 /* Jump to one after the next slash if there is any. */
181 path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr)
182 {
183 /* Note that this also treats backslashes as component separators, which
184 * helps in cleaning up backslash-separated paths. */
185 next_slash_ptr = BLI_path_slash_find(path_component);
186
187 const bool is_last_component = next_slash_ptr == nullptr;
188 /* Note that this won't be null terminated. */
189 const StringRef component_name = is_last_component ?
190 path_component :
191 StringRef(path_component,
192 next_slash_ptr - path_component);
193
194 callback(component_name, is_last_component);
195 }
196}
197
199 const AssetCatalogPath &to_path) const
200{
201 if (!from_path) {
202 if (!to_path) {
203 return AssetCatalogPath("");
204 }
205 return to_path / *this;
206 }
207
208 if (!this->is_contained_in(from_path)) {
209 return AssetCatalogPath("");
210 }
211
212 if (*this == from_path) {
213 /* Early return, because otherwise the length+1 below is going to cause problems. */
214 return to_path;
215 }
216
217 /* When from_path = "test", we need to skip "test/" to get the rest of the path, hence the +1. */
218 const StringRef suffix = StringRef(path_).substr(from_path.length() + 1);
219 const AssetCatalogPath path_suffix(suffix);
220 return to_path / path_suffix;
221}
222
223} // namespace blender::asset_system
const char * BLI_path_slash_find(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr bool startswith(StringRef prefix) const
constexpr StringRef trim() const
AssetCatalogPath rebase(const AssetCatalogPath &from_path, const AssetCatalogPath &to_path) const
AssetCatalogPath operator/(const AssetCatalogPath &path_to_append) const
bool operator==(const AssetCatalogPath &other_path) const
bool operator!=(const AssetCatalogPath &other_path) const
bool is_contained_in(const AssetCatalogPath &other_path) const
void iterate_components(ComponentIteratorFn callback) const
bool operator<(const AssetCatalogPath &other_path) const
static std::string cleanup_component(StringRef component_name)
DEGForeachIDComponentCallback callback
std::ostream & operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append)
unsigned __int64 uint64_t
Definition stdint.h:90