Skip to content

Commit bf20d86

Browse files
committed
folder: support creating folder path
1 parent a906cea commit bf20d86

File tree

6 files changed

+223
-1
lines changed

6 files changed

+223
-1
lines changed

include/vxcore/vxcore.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ VXCORE_API VxCoreError vxcore_folder_create(VxCoreContextHandle context, const c
5656
const char *parent_path, const char *folder_name,
5757
char **out_folder_id);
5858

59+
VXCORE_API VxCoreError vxcore_folder_create_path(VxCoreContextHandle context,
60+
const char *notebook_id, const char *folder_path,
61+
char **out_folder_id);
62+
5963
VXCORE_API VxCoreError vxcore_folder_delete(VxCoreContextHandle context, const char *notebook_id,
6064
const char *folder_path);
6165

src/api/vxcore_folder_api.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,36 @@ VXCORE_API VxCoreError vxcore_folder_create(VxCoreContextHandle context, const c
4242
}
4343
}
4444

45+
VXCORE_API VxCoreError vxcore_folder_create_path(VxCoreContextHandle context,
46+
const char *notebook_id, const char *folder_path,
47+
char **out_folder_id) {
48+
if (!context || !notebook_id || !folder_path || !out_folder_id) {
49+
return VXCORE_ERR_INVALID_PARAM;
50+
}
51+
52+
vxcore::VxCoreContext *ctx = reinterpret_cast<vxcore::VxCoreContext *>(context);
53+
54+
try {
55+
vxcore::Notebook *notebook = ctx->notebook_manager->GetNotebook(notebook_id);
56+
if (!notebook) {
57+
ctx->last_error = "Notebook not found";
58+
return VXCORE_ERR_NOT_FOUND;
59+
}
60+
61+
std::string folder_id;
62+
VxCoreError error = notebook->CreateFolderPath(folder_path, folder_id);
63+
if (error != VXCORE_OK) {
64+
return error;
65+
}
66+
67+
*out_folder_id = vxcore_strdup(folder_id.c_str());
68+
return VXCORE_OK;
69+
} catch (const std::exception &e) {
70+
ctx->last_error = std::string("Exception: ") + e.what();
71+
return VXCORE_ERR_UNKNOWN;
72+
}
73+
}
74+
4575
VXCORE_API VxCoreError vxcore_folder_delete(VxCoreContextHandle context, const char *notebook_id,
4676
const char *folder_path) {
4777
if (!context || !notebook_id || !folder_path) {

src/core/bundled_folder_manager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,7 @@ void BundledFolderManager::IterateAllFiles(
10201020
}
10211021

10221022
for (const auto &folder_name : config->folders) {
1023-
std::string child_path = fp.empty() ? folder_name : fp + "/" + folder_name;
1023+
std::string child_path = ConcatenatePaths(fp, folder_name);
10241024
if (!iterate_folder(child_path)) {
10251025
return false;
10261026
}

src/core/notebook.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,41 @@ VxCoreError Notebook::CreateTag(const std::string &tag_name, const std::string &
169169
return VXCORE_OK;
170170
}
171171

172+
VxCoreError Notebook::CreateFolderPath(const std::string &folder_path, std::string &out_folder_id) {
173+
if (folder_path.empty()) {
174+
return VXCORE_ERR_INVALID_PARAM;
175+
}
176+
177+
if (!folder_manager_) {
178+
return VXCORE_ERR_INVALID_STATE;
179+
}
180+
181+
std::vector<std::string> path_components = SplitPathComponents(folder_path);
182+
if (path_components.empty()) {
183+
return VXCORE_ERR_INVALID_PARAM;
184+
}
185+
186+
std::string current_parent = ".";
187+
std::string folder_id;
188+
189+
for (size_t i = 0; i < path_components.size(); ++i) {
190+
const std::string &folder_name = path_components[i];
191+
if (folder_name.empty()) {
192+
return VXCORE_ERR_INVALID_PARAM;
193+
}
194+
195+
VxCoreError create_err = folder_manager_->CreateFolder(current_parent, folder_name, folder_id);
196+
if (create_err != VXCORE_OK && create_err != VXCORE_ERR_ALREADY_EXISTS) {
197+
return create_err;
198+
}
199+
200+
current_parent = ConcatenatePaths(current_parent, folder_name);
201+
}
202+
203+
out_folder_id = folder_id;
204+
return VXCORE_OK;
205+
}
206+
172207
VxCoreError Notebook::CreateTagPath(const std::string &tag_path) {
173208
if (tag_path.empty()) {
174209
return VXCORE_ERR_INVALID_PARAM;

src/core/notebook.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class Notebook {
6767

6868
FolderManager *GetFolderManager() { return folder_manager_.get(); }
6969

70+
VxCoreError CreateFolderPath(const std::string &folder_path, std::string &out_folder_id);
71+
7072
VxCoreError CreateTag(const std::string &tag_name, const std::string &parent_tag = "");
7173
VxCoreError CreateTagPath(const std::string &tag_path);
7274
VxCoreError DeleteTag(const std::string &tag_name);

tests/test_folder.cpp

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,152 @@ int test_file_tag_invalid_params() {
11351135
return 0;
11361136
}
11371137

1138+
int test_folder_create_path_basic() {
1139+
std::cout << " Running test_folder_create_path_basic..." << std::endl;
1140+
cleanup_test_dir("test_folder_path_basic_nb");
1141+
1142+
VxCoreContextHandle ctx = nullptr;
1143+
VxCoreError err = vxcore_context_create(nullptr, &ctx);
1144+
ASSERT_EQ(err, VXCORE_OK);
1145+
1146+
char *notebook_id = nullptr;
1147+
err = vxcore_notebook_create(ctx, "test_folder_path_basic_nb", "{\"name\":\"Test Notebook\"}",
1148+
VXCORE_NOTEBOOK_BUNDLED, &notebook_id);
1149+
ASSERT_EQ(err, VXCORE_OK);
1150+
1151+
char *folder_id = nullptr;
1152+
err = vxcore_folder_create_path(ctx, notebook_id, "level1/level2/level3", &folder_id);
1153+
ASSERT_EQ(err, VXCORE_OK);
1154+
ASSERT_NOT_NULL(folder_id);
1155+
1156+
ASSERT(path_exists("test_folder_path_basic_nb/level1"));
1157+
ASSERT(path_exists("test_folder_path_basic_nb/level1/level2"));
1158+
ASSERT(path_exists("test_folder_path_basic_nb/level1/level2/level3"));
1159+
1160+
vxcore_string_free(folder_id);
1161+
vxcore_string_free(notebook_id);
1162+
vxcore_context_destroy(ctx);
1163+
cleanup_test_dir("test_folder_path_basic_nb");
1164+
std::cout << " ✓ test_folder_create_path_basic passed" << std::endl;
1165+
return 0;
1166+
}
1167+
1168+
int test_folder_create_path_single() {
1169+
std::cout << " Running test_folder_create_path_single..." << std::endl;
1170+
cleanup_test_dir("test_folder_path_single_nb");
1171+
1172+
VxCoreContextHandle ctx = nullptr;
1173+
VxCoreError err = vxcore_context_create(nullptr, &ctx);
1174+
ASSERT_EQ(err, VXCORE_OK);
1175+
1176+
char *notebook_id = nullptr;
1177+
err = vxcore_notebook_create(ctx, "test_folder_path_single_nb", "{\"name\":\"Test Notebook\"}",
1178+
VXCORE_NOTEBOOK_BUNDLED, &notebook_id);
1179+
ASSERT_EQ(err, VXCORE_OK);
1180+
1181+
char *folder_id = nullptr;
1182+
err = vxcore_folder_create_path(ctx, notebook_id, "single_folder", &folder_id);
1183+
ASSERT_EQ(err, VXCORE_OK);
1184+
ASSERT_NOT_NULL(folder_id);
1185+
1186+
ASSERT(path_exists("test_folder_path_single_nb/single_folder"));
1187+
1188+
vxcore_string_free(folder_id);
1189+
vxcore_string_free(notebook_id);
1190+
vxcore_context_destroy(ctx);
1191+
cleanup_test_dir("test_folder_path_single_nb");
1192+
std::cout << " ✓ test_folder_create_path_single passed" << std::endl;
1193+
return 0;
1194+
}
1195+
1196+
int test_folder_create_path_partial_exists() {
1197+
std::cout << " Running test_folder_create_path_partial_exists..." << std::endl;
1198+
cleanup_test_dir("test_folder_path_partial_nb");
1199+
1200+
VxCoreContextHandle ctx = nullptr;
1201+
VxCoreError err = vxcore_context_create(nullptr, &ctx);
1202+
ASSERT_EQ(err, VXCORE_OK);
1203+
1204+
char *notebook_id = nullptr;
1205+
err = vxcore_notebook_create(ctx, "test_folder_path_partial_nb", "{\"name\":\"Test Notebook\"}",
1206+
VXCORE_NOTEBOOK_BUNDLED, &notebook_id);
1207+
ASSERT_EQ(err, VXCORE_OK);
1208+
1209+
char *folder_id = nullptr;
1210+
err = vxcore_folder_create(ctx, notebook_id, ".", "existing", &folder_id);
1211+
ASSERT_EQ(err, VXCORE_OK);
1212+
vxcore_string_free(folder_id);
1213+
1214+
err = vxcore_folder_create_path(ctx, notebook_id, "existing/new1/new2", &folder_id);
1215+
ASSERT_EQ(err, VXCORE_OK);
1216+
ASSERT_NOT_NULL(folder_id);
1217+
1218+
ASSERT(path_exists("test_folder_path_partial_nb/existing"));
1219+
ASSERT(path_exists("test_folder_path_partial_nb/existing/new1"));
1220+
ASSERT(path_exists("test_folder_path_partial_nb/existing/new1/new2"));
1221+
1222+
vxcore_string_free(folder_id);
1223+
vxcore_string_free(notebook_id);
1224+
vxcore_context_destroy(ctx);
1225+
cleanup_test_dir("test_folder_path_partial_nb");
1226+
std::cout << " ✓ test_folder_create_path_partial_exists passed" << std::endl;
1227+
return 0;
1228+
}
1229+
1230+
int test_folder_create_path_empty() {
1231+
std::cout << " Running test_folder_create_path_empty..." << std::endl;
1232+
cleanup_test_dir("test_folder_path_empty_nb");
1233+
1234+
VxCoreContextHandle ctx = nullptr;
1235+
VxCoreError err = vxcore_context_create(nullptr, &ctx);
1236+
ASSERT_EQ(err, VXCORE_OK);
1237+
1238+
char *notebook_id = nullptr;
1239+
err = vxcore_notebook_create(ctx, "test_folder_path_empty_nb", "{\"name\":\"Test Notebook\"}",
1240+
VXCORE_NOTEBOOK_BUNDLED, &notebook_id);
1241+
ASSERT_EQ(err, VXCORE_OK);
1242+
1243+
char *folder_id = nullptr;
1244+
err = vxcore_folder_create_path(ctx, notebook_id, "", &folder_id);
1245+
ASSERT_EQ(err, VXCORE_ERR_INVALID_PARAM);
1246+
ASSERT_NULL(folder_id);
1247+
1248+
vxcore_string_free(notebook_id);
1249+
vxcore_context_destroy(ctx);
1250+
cleanup_test_dir("test_folder_path_empty_nb");
1251+
std::cout << " ✓ test_folder_create_path_empty passed" << std::endl;
1252+
return 0;
1253+
}
1254+
1255+
int test_folder_create_path_trailing_slash() {
1256+
std::cout << " Running test_folder_create_path_trailing_slash..." << std::endl;
1257+
cleanup_test_dir("test_folder_path_slash_nb");
1258+
1259+
VxCoreContextHandle ctx = nullptr;
1260+
VxCoreError err = vxcore_context_create(nullptr, &ctx);
1261+
ASSERT_EQ(err, VXCORE_OK);
1262+
1263+
char *notebook_id = nullptr;
1264+
err = vxcore_notebook_create(ctx, "test_folder_path_slash_nb", "{\"name\":\"Test Notebook\"}",
1265+
VXCORE_NOTEBOOK_BUNDLED, &notebook_id);
1266+
ASSERT_EQ(err, VXCORE_OK);
1267+
1268+
char *folder_id = nullptr;
1269+
err = vxcore_folder_create_path(ctx, notebook_id, "path1/path2/", &folder_id);
1270+
ASSERT_EQ(err, VXCORE_OK);
1271+
ASSERT_NOT_NULL(folder_id);
1272+
1273+
ASSERT(path_exists("test_folder_path_slash_nb/path1"));
1274+
ASSERT(path_exists("test_folder_path_slash_nb/path1/path2"));
1275+
1276+
vxcore_string_free(folder_id);
1277+
vxcore_string_free(notebook_id);
1278+
vxcore_context_destroy(ctx);
1279+
cleanup_test_dir("test_folder_path_slash_nb");
1280+
std::cout << " ✓ test_folder_create_path_trailing_slash passed" << std::endl;
1281+
return 0;
1282+
}
1283+
11381284
int main() {
11391285
std::cout << "Running folder tests..." << std::endl;
11401286

@@ -1173,6 +1319,11 @@ int main() {
11731319
RUN_TEST(test_file_tag_duplicate);
11741320
RUN_TEST(test_file_untag_not_found);
11751321
RUN_TEST(test_file_tag_invalid_params);
1322+
RUN_TEST(test_folder_create_path_basic);
1323+
RUN_TEST(test_folder_create_path_single);
1324+
RUN_TEST(test_folder_create_path_partial_exists);
1325+
RUN_TEST(test_folder_create_path_empty);
1326+
RUN_TEST(test_folder_create_path_trailing_slash);
11761327

11771328
std::cout << "✓ All folder tests passed" << std::endl;
11781329
return 0;

0 commit comments

Comments
 (0)