Asset Catalog Service: add function to change catalog path

Add `AssetCatalogService::update_catalog_path()` to change the catalog
path of the given catalog, and also change the path of all the catalogs
contained within the given catalog.

Rebuilds the tree structure for the UI, but does not save the new catalog
definitions to disk.

No user-facing changes, just backend preparation for UI work.
This commit is contained in:
Sybren A. Stüvel 2021-09-28 16:07:18 +02:00
parent d2004326a1
commit 52a702468a
3 changed files with 103 additions and 0 deletions

View File

@ -118,6 +118,11 @@ class AssetCatalogService {
* written. */
void delete_catalog(CatalogID catalog_id);
/**
* Update the catalog path, also updating the catalog path of all sub-catalogs.
*/
void update_catalog_path(CatalogID catalog_id, const CatalogPath &new_catalog_path);
AssetCatalogTree *get_catalog_tree();
/** Return true only if there are no catalogs known. */
@ -300,6 +305,15 @@ class AssetCatalog {
bool is_deleted = false;
} flags;
/**
* \return true only if this catalog's path is contained within the given path.
* When this catalog's path is equal to the given path, return true as well.
*
* Note that non-normalized paths (so for example starting or ending with a slash) are not
* supported, and result in undefined behaviour.
*/
bool is_contained_in(const CatalogPath &other_path) const;
/**
* Create a new Catalog with the given path, auto-generating a sensible catalog simple-name.
*

View File

@ -99,6 +99,25 @@ void AssetCatalogService::delete_catalog(CatalogID catalog_id)
this->rebuild_tree();
}
void AssetCatalogService::update_catalog_path(CatalogID catalog_id,
const CatalogPath &new_catalog_path)
{
AssetCatalog *renamed_cat = this->find_catalog(catalog_id);
const CatalogPath old_cat_path = renamed_cat->path;
for (auto &catalog_uptr : catalogs_.values()) {
AssetCatalog *cat = catalog_uptr.get();
if (!cat->is_contained_in(old_cat_path)) {
continue;
}
const CatalogPath path_suffix = cat->path.substr(old_cat_path.length());
cat->path = new_catalog_path + path_suffix;
}
this->rebuild_tree();
}
AssetCatalog *AssetCatalogService::create_catalog(const CatalogPath &catalog_path)
{
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path(catalog_path);
@ -756,4 +775,26 @@ CatalogPath AssetCatalog::cleanup_path(const CatalogPath &path)
return clean_path;
}
bool AssetCatalog::is_contained_in(const CatalogPath &other_path) const
{
if (other_path.empty()) {
return true;
}
if (this->path == other_path) {
return true;
}
/* To be a child path of 'other_path', our path must be at least a separator and another
* character longer. */
if (this->path.length() < other_path.length() + 2) {
return false;
}
const StringRef this_path(this->path);
const bool prefix_ok = this_path.startswith(other_path);
const char next_char = this_path[other_path.length()];
return prefix_ok && next_char == AssetCatalogService::PATH_SEPARATOR;
}
} // namespace blender::bke

View File

@ -711,6 +711,37 @@ TEST_F(AssetCatalogTest, delete_catalog_write_to_disk)
EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE));
}
TEST_F(AssetCatalogTest, update_catalog_path)
{
AssetCatalogService service(asset_library_root_);
service.load_from_disk(asset_library_root_ + "/" +
AssetCatalogService::DEFAULT_CATALOG_FILENAME);
const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA);
const CatalogPath orig_path = orig_cat->path;
service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena");
EXPECT_EQ(nullptr, service.find_catalog_by_path(orig_path))
<< "The original (pre-rename) path should not be associated with a catalog any more.";
const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA);
ASSERT_NE(nullptr, renamed_cat);
ASSERT_EQ(orig_cat, renamed_cat) << "Changing the path should not reallocate the catalog.";
EXPECT_EQ(orig_cat->simple_name, renamed_cat->simple_name)
<< "Changing the path should not change the simple name.";
EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id)
<< "Changing the path should not change the catalog ID.";
EXPECT_EQ("charlib/Ružena", renamed_cat->path)
<< "Changing the path should change the path. Surprise.";
EXPECT_EQ("charlib/Ružena/hand", service.find_catalog(UUID_POSES_RUZENA_HAND)->path)
<< "Changing the path should update children.";
EXPECT_EQ("charlib/Ružena/face", service.find_catalog(UUID_POSES_RUZENA_FACE)->path)
<< "Changing the path should update children.";
}
TEST_F(AssetCatalogTest, merge_catalog_files)
{
const CatalogFilePath cdf_dir = create_temp_path();
@ -813,4 +844,21 @@ TEST_F(AssetCatalogTest, order_by_path)
}
}
TEST_F(AssetCatalogTest, is_contained_in)
{
const AssetCatalog cat(BLI_uuid_generate_random(), "simple/path/child", "");
EXPECT_FALSE(cat.is_contained_in("unrelated"));
EXPECT_FALSE(cat.is_contained_in("sim"));
EXPECT_FALSE(cat.is_contained_in("simple/pathx"));
EXPECT_FALSE(cat.is_contained_in("simple/path/c"));
EXPECT_FALSE(cat.is_contained_in("simple/path/child/grandchild"));
EXPECT_FALSE(cat.is_contained_in("simple/path/"))
<< "Non-normalized paths are not expected to work.";
EXPECT_TRUE(cat.is_contained_in(""));
EXPECT_TRUE(cat.is_contained_in("simple"));
EXPECT_TRUE(cat.is_contained_in("simple/path"));
}
} // namespace blender::bke::tests