Skip to content

Commit deb2e72

Browse files
committed
support notebook recycle bin
1 parent 551bdae commit deb2e72

File tree

11 files changed

+495
-2
lines changed

11 files changed

+495
-2
lines changed

include/vxcore/vxcore.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ VXCORE_API VxCoreError vxcore_notebook_update_config(VxCoreContextHandle context
8080
VXCORE_API VxCoreError vxcore_notebook_rebuild_cache(VxCoreContextHandle context,
8181
const char *notebook_id);
8282

83+
// ============ Recycle Bin Operations (Bundled Notebooks Only) ============
84+
// Get the path to the recycle bin folder.
85+
// Returns empty string for raw notebooks (not supported).
86+
VXCORE_API VxCoreError vxcore_notebook_get_recycle_bin_path(VxCoreContextHandle context,
87+
const char *notebook_id,
88+
char **out_path);
89+
90+
// Empty the recycle bin by deleting all files and folders in it.
91+
// Returns VXCORE_ERR_UNSUPPORTED for raw notebooks.
92+
VXCORE_API VxCoreError vxcore_notebook_empty_recycle_bin(VxCoreContextHandle context,
93+
const char *notebook_id);
94+
8395
// ============ Folder Operations ============
8496
VXCORE_API VxCoreError vxcore_folder_create(VxCoreContextHandle context, const char *notebook_id,
8597
const char *parent_path, const char *folder_name,

src/api/vxcore_notebook_api.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,59 @@ VXCORE_API VxCoreError vxcore_notebook_rebuild_cache(VxCoreContextHandle context
199199
return VXCORE_ERR_UNKNOWN;
200200
}
201201
}
202+
203+
VXCORE_API VxCoreError vxcore_notebook_get_recycle_bin_path(VxCoreContextHandle context,
204+
const char *notebook_id,
205+
char **out_path) {
206+
if (!context || !notebook_id || !out_path) {
207+
return VXCORE_ERR_NULL_POINTER;
208+
}
209+
210+
auto *ctx = reinterpret_cast<vxcore::VxCoreContext *>(context);
211+
212+
try {
213+
auto *notebook = ctx->notebook_manager->GetNotebook(notebook_id);
214+
if (!notebook) {
215+
ctx->last_error = "Notebook not found";
216+
return VXCORE_ERR_NOT_FOUND;
217+
}
218+
219+
std::string path = notebook->GetRecycleBinPath();
220+
char *path_copy = vxcore_strdup(path.c_str());
221+
if (!path_copy) {
222+
return VXCORE_ERR_OUT_OF_MEMORY;
223+
}
224+
225+
*out_path = path_copy;
226+
return VXCORE_OK;
227+
} catch (...) {
228+
ctx->last_error = "Unknown error getting recycle bin path";
229+
return VXCORE_ERR_UNKNOWN;
230+
}
231+
}
232+
233+
VXCORE_API VxCoreError vxcore_notebook_empty_recycle_bin(VxCoreContextHandle context,
234+
const char *notebook_id) {
235+
if (!context || !notebook_id) {
236+
return VXCORE_ERR_NULL_POINTER;
237+
}
238+
239+
auto *ctx = reinterpret_cast<vxcore::VxCoreContext *>(context);
240+
241+
try {
242+
auto *notebook = ctx->notebook_manager->GetNotebook(notebook_id);
243+
if (!notebook) {
244+
ctx->last_error = "Notebook not found";
245+
return VXCORE_ERR_NOT_FOUND;
246+
}
247+
248+
VxCoreError err = notebook->EmptyRecycleBin();
249+
if (err != VXCORE_OK) {
250+
ctx->last_error = "Failed to empty recycle bin";
251+
}
252+
return err;
253+
} catch (...) {
254+
ctx->last_error = "Unknown error emptying recycle bin";
255+
return VXCORE_ERR_UNKNOWN;
256+
}
257+
}

src/core/bundled_folder_manager.cpp

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,10 +382,18 @@ VxCoreError BundledFolderManager::DeleteFolder(const std::string &folder_path) {
382382
}
383383

384384
try {
385+
// Move content folder to recycle bin instead of permanent delete
386+
VxCoreError move_error = MoveToRecycleBin(fs::path(content_path));
387+
if (move_error != VXCORE_OK) {
388+
VXCORE_LOG_WARN("Failed to move folder to recycle bin, falling back to delete: %s",
389+
content_path.c_str());
390+
fs::remove_all(content_path);
391+
}
392+
393+
// Always permanently delete the config folder (vx.json metadata)
385394
if (fs::exists(config_path)) {
386395
fs::remove_all(fs::path(config_path).parent_path());
387396
}
388-
fs::remove_all(content_path);
389397

390398
// Write-through to MetadataStore
391399
if (auto *store = notebook_->GetMetadataStore(); store && !folder_id.empty()) {
@@ -824,7 +832,14 @@ VxCoreError BundledFolderManager::DeleteFile(const std::string &file_path) {
824832
try {
825833
std::string content_path = GetContentPath(folder_path);
826834
fs::path fs_file_path = fs::path(content_path) / file_name;
827-
fs::remove(fs_file_path);
835+
836+
// Move to recycle bin instead of permanent delete
837+
VxCoreError move_error = MoveToRecycleBin(fs_file_path);
838+
if (move_error != VXCORE_OK) {
839+
VXCORE_LOG_WARN("Failed to move file to recycle bin, falling back to delete: %s",
840+
fs_file_path.string().c_str());
841+
fs::remove(fs_file_path);
842+
}
828843

829844
// Write-through to MetadataStore
830845
if (auto *store = notebook_->GetMetadataStore(); !file_id.empty()) {
@@ -1541,4 +1556,80 @@ void BundledFolderManager::SyncFolderToStore(const std::string &folder_path,
15411556
config.id.c_str(), folder_path.c_str());
15421557
}
15431558

1559+
1560+
std::string BundledFolderManager::GetRecycleBinPath() const {
1561+
return ConcatenatePaths(notebook_->GetMetadataFolder(), "recycle_bin");
1562+
}
1563+
1564+
std::string BundledFolderManager::GenerateUniqueRecycleBinName(const std::string &name) const {
1565+
fs::path recycle_bin_path(GetRecycleBinPath());
1566+
fs::path dest_path = recycle_bin_path / name;
1567+
1568+
if (!fs::exists(dest_path)) {
1569+
return name;
1570+
}
1571+
1572+
// Extract base name and extension
1573+
std::string base_name = name;
1574+
std::string extension;
1575+
size_t dot_pos = name.find_last_of('.');
1576+
if (dot_pos != std::string::npos && dot_pos > 0) {
1577+
base_name = name.substr(0, dot_pos);
1578+
extension = name.substr(dot_pos);
1579+
}
1580+
1581+
// Try _1, _2, _3, etc. until we find a unique name
1582+
int suffix = 1;
1583+
while (true) {
1584+
std::string new_name = base_name + "_" + std::to_string(suffix) + extension;
1585+
dest_path = recycle_bin_path / new_name;
1586+
if (!fs::exists(dest_path)) {
1587+
return new_name;
1588+
}
1589+
++suffix;
1590+
if (suffix > 10000) {
1591+
// Safety limit to prevent infinite loop
1592+
VXCORE_LOG_ERROR("GenerateUniqueRecycleBinName: Too many conflicts for %s", name.c_str());
1593+
return base_name + "_" + std::to_string(GetCurrentTimestampMillis()) + extension;
1594+
}
1595+
}
1596+
}
1597+
1598+
VxCoreError BundledFolderManager::MoveToRecycleBin(const std::filesystem::path &source_path) {
1599+
if (!fs::exists(source_path)) {
1600+
VXCORE_LOG_WARN("MoveToRecycleBin: Source does not exist: %s", source_path.string().c_str());
1601+
return VXCORE_ERR_NOT_FOUND;
1602+
}
1603+
1604+
fs::path recycle_bin_path(GetRecycleBinPath());
1605+
1606+
// Ensure recycle bin directory exists
1607+
try {
1608+
if (!fs::exists(recycle_bin_path)) {
1609+
fs::create_directories(recycle_bin_path);
1610+
VXCORE_LOG_INFO("MoveToRecycleBin: Created recycle bin at %s",
1611+
recycle_bin_path.string().c_str());
1612+
}
1613+
} catch (const std::exception &e) {
1614+
VXCORE_LOG_ERROR("MoveToRecycleBin: Failed to create recycle bin: %s", e.what());
1615+
return VXCORE_ERR_IO;
1616+
}
1617+
1618+
// Generate unique name in recycle bin
1619+
std::string original_name = source_path.filename().string();
1620+
std::string unique_name = GenerateUniqueRecycleBinName(original_name);
1621+
fs::path dest_path = recycle_bin_path / unique_name;
1622+
1623+
// Move the file/folder to recycle bin
1624+
try {
1625+
fs::rename(source_path, dest_path);
1626+
VXCORE_LOG_INFO("MoveToRecycleBin: Moved %s to %s", source_path.string().c_str(),
1627+
dest_path.string().c_str());
1628+
return VXCORE_OK;
1629+
} catch (const std::exception &e) {
1630+
VXCORE_LOG_ERROR("MoveToRecycleBin: Failed to move %s: %s", source_path.string().c_str(),
1631+
e.what());
1632+
return VXCORE_ERR_IO;
1633+
}
1634+
}
15441635
} // namespace vxcore

src/core/bundled_folder_manager.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define VXCORE_BUNDLED_FOLDER_MANAGER_H
33

44
#include <map>
5+
#include <filesystem>
56
#include <memory>
67
#include <string>
78

@@ -85,6 +86,9 @@ class BundledFolderManager : public FolderManager {
8586
// Returns VXCORE_OK on success
8687
VxCoreError SyncMetadataStoreFromConfigs();
8788

89+
// Returns the path to the recycle bin folder
90+
std::string GetRecycleBinPath() const;
91+
8892
private:
8993
VxCoreError GetFolderConfig(const std::string &folder_path, FolderConfig **out_config,
9094
const std::string *parent_id = nullptr);
@@ -109,6 +113,9 @@ class BundledFolderManager : public FolderManager {
109113
// Get the parent folder's ID (UUID) for a given folder path
110114
std::string GetParentFolderId(const std::string &folder_path);
111115

116+
// Recycle bin helpers
117+
std::string GenerateUniqueRecycleBinName(const std::string &name) const;
118+
VxCoreError MoveToRecycleBin(const std::filesystem::path &source_path);
112119
std::map<std::string, std::unique_ptr<FolderConfig>> config_cache_;
113120
};
114121

src/core/bundled_notebook.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,31 @@ VxCoreError BundledNotebook::RebuildCache() {
186186
return bundled_folder_manager->SyncMetadataStoreFromConfigs();
187187
}
188188

189+
std::string BundledNotebook::GetRecycleBinPath() const {
190+
auto *bundled_folder_manager = dynamic_cast<BundledFolderManager *>(folder_manager_.get());
191+
if (!bundled_folder_manager) {
192+
VXCORE_LOG_ERROR("GetRecycleBinPath: folder_manager is not BundledFolderManager");
193+
return "";
194+
}
195+
return bundled_folder_manager->GetRecycleBinPath();
196+
}
197+
198+
VxCoreError BundledNotebook::EmptyRecycleBin() {
199+
std::string recycle_bin_path = GetRecycleBinPath();
200+
try {
201+
std::filesystem::path rb_path(recycle_bin_path);
202+
if (!std::filesystem::exists(rb_path)) {
203+
return VXCORE_OK; // Nothing to empty
204+
}
205+
for (const auto &entry : std::filesystem::directory_iterator(rb_path)) {
206+
std::filesystem::remove_all(entry.path());
207+
}
208+
VXCORE_LOG_INFO("EmptyRecycleBin: Cleared recycle bin at %s", recycle_bin_path.c_str());
209+
return VXCORE_OK;
210+
} catch (const std::exception &e) {
211+
VXCORE_LOG_ERROR("EmptyRecycleBin: Failed to empty recycle bin: %s", e.what());
212+
return VXCORE_ERR_IO;
213+
}
214+
}
215+
189216
} // namespace vxcore

src/core/bundled_notebook.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class BundledNotebook : public Notebook {
1919
VxCoreError UpdateConfig(const NotebookConfig &config) override;
2020
VxCoreError RebuildCache() override;
2121

22+
std::string GetRecycleBinPath() const override;
23+
VxCoreError EmptyRecycleBin() override;
24+
2225
private:
2326
BundledNotebook(const std::string &local_data_folder, const std::string &root_folder);
2427

src/core/notebook.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ class Notebook {
6767
std::string GetLocalDataFolder() const;
6868
virtual std::string GetMetadataFolder() const = 0;
6969

70+
// Recycle bin operations (bundled notebooks only)
71+
// Returns the path to the recycle bin folder, or empty string if not supported.
72+
virtual std::string GetRecycleBinPath() const = 0;
73+
// Empties the recycle bin by deleting all files and folders in it.
74+
// Returns VXCORE_OK on success, VXCORE_ERR_NOT_SUPPORTED for raw notebooks.
75+
virtual VxCoreError EmptyRecycleBin() = 0;
76+
7077
FolderManager *GetFolderManager() { return folder_manager_.get(); }
7178
MetadataStore *GetMetadataStore() { return metadata_store_.get(); }
7279

src/core/raw_notebook.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,14 @@ VxCoreError RawNotebook::RebuildCache() {
154154
return VXCORE_OK;
155155
}
156156

157+
std::string RawNotebook::GetRecycleBinPath() const {
158+
// Raw notebooks do not support recycle bin
159+
return "";
160+
}
161+
162+
VxCoreError RawNotebook::EmptyRecycleBin() {
163+
// Raw notebooks do not support recycle bin
164+
return VXCORE_ERR_UNSUPPORTED;
165+
}
166+
157167
} // namespace vxcore

src/core/raw_notebook.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class RawNotebook : public Notebook {
1717
VxCoreError UpdateConfig(const NotebookConfig &config) override;
1818
VxCoreError RebuildCache() override;
1919

20+
std::string GetRecycleBinPath() const override;
21+
VxCoreError EmptyRecycleBin() override;
22+
2023
private:
2124
RawNotebook(const std::string &local_data_folder, const std::string &root_folder);
2225

0 commit comments

Comments
 (0)