Convert notebook list to tree
Use that tree to build the delete list. I think it's not necessary as files marked as deleted are direct children of root always, but, just in case. Now if an ancestor is marked as deleted the whole subtree gets printed out for deletion. Also support multi root, rubbish bin seems to be its own root in fact.
This commit is contained in:
parent
7548688eda
commit
2ce7f4cfaf
1 changed files with 195 additions and 15 deletions
210
main.cpp
210
main.cpp
|
@ -1,3 +1,20 @@
|
|||
/* Copyright 2021, Michele Santullo
|
||||
* This file is part of remarkable_tool.
|
||||
*
|
||||
* remarkable_tool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Remarkable_tool is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with remarkable_tool. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <rmlab/rmlab.hpp>
|
||||
#include <simdjson.h>
|
||||
|
@ -6,6 +23,9 @@
|
|||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
@ -23,7 +43,27 @@ struct NotebookInfo {
|
|||
bool deleted = false;
|
||||
};
|
||||
|
||||
std::ostream& operator<< (std::ostream& str, const std::pair<std::string, NotebookInfo>& v) {
|
||||
struct Node {
|
||||
Node() :
|
||||
info(nullptr),
|
||||
parent(nullptr)
|
||||
{ }
|
||||
|
||||
Node (std::string_view name, const NotebookInfo* payload, Node* parent) :
|
||||
name(name),
|
||||
info(payload),
|
||||
parent(parent)
|
||||
{ }
|
||||
|
||||
std::vector<Node> children;
|
||||
std::string_view name;
|
||||
const NotebookInfo* info;
|
||||
const Node* parent;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<std::string, NotebookInfo> NotebookMapType;
|
||||
|
||||
std::ostream& operator<< (std::ostream& str, const NotebookMapType::value_type& v) {
|
||||
auto& key = v.first;
|
||||
auto& val = v.second;
|
||||
str << key << " (\"" << val.visible_name << "\", " << val.type <<
|
||||
|
@ -43,11 +83,17 @@ std::ostream& operator<< (std::ostream& str, const std::pair<std::string, Notebo
|
|||
return str;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, NotebookInfo> build_notebook_infos (
|
||||
template <typename T>
|
||||
std::vector<T>& operator+= (std::vector<T>& dst, const std::vector<T>& src) {
|
||||
dst.insert(dst.end(), src.begin(), src.end());
|
||||
return dst;
|
||||
}
|
||||
|
||||
NotebookMapType build_notebook_infos (
|
||||
const fs::path& from_path
|
||||
) {
|
||||
simdjson::dom::parser parser;
|
||||
std::unordered_map<std::string, NotebookInfo> retval;
|
||||
NotebookMapType retval;
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(from_path)) {
|
||||
NotebookInfo& curr_info = retval[entry.path().stem()];
|
||||
|
@ -76,29 +122,163 @@ std::unordered_map<std::string, NotebookInfo> build_notebook_infos (
|
|||
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::vector<Node> to_tree (const NotebookMapType& nbs) {
|
||||
std::unordered_map<std::string_view, std::pair<unsigned int, Node>> parents;
|
||||
for (const auto& nb : nbs) {
|
||||
auto it_ins = parents.emplace(nb.second.parent, std::make_pair(0U, Node{nb.second.parent, nullptr, nullptr}));
|
||||
it_ins.first->second.first++;
|
||||
}
|
||||
for (const auto& nb : nbs) {
|
||||
auto it_parent = parents.find(nb.first);
|
||||
if (parents.cend() != it_parent) {
|
||||
Node& node = it_parent->second.second;
|
||||
node.info = &nb.second;
|
||||
}
|
||||
}
|
||||
for (auto& par : parents) {
|
||||
Node& node = par.second.second;
|
||||
const unsigned int children_count = par.second.first;
|
||||
node.children.reserve(children_count);
|
||||
}
|
||||
|
||||
for (const auto& nb : nbs) {
|
||||
if (0 == parents.count(nb.first)) {
|
||||
Node& parent = parents[nb.second.parent].second;
|
||||
parent.children.emplace_back(nb.first, &nb.second, &parent);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Node> roots;
|
||||
std::unordered_map<std::string_view, Node*> grouped_nodes;
|
||||
for (auto it = parents.begin(), it_end = parents.end(); it != it_end; it = parents.erase(it)) {
|
||||
auto& node = it->second.second;
|
||||
if (node.info) {
|
||||
auto it_parent = parents.find(node.info->parent);
|
||||
Node* dst_node = (parents.end() == it_parent ? grouped_nodes[node.info->parent] : &it_parent->second.second);
|
||||
dst_node->children.push_back(std::move(node));
|
||||
grouped_nodes[dst_node->children.back().name] = &dst_node->children.back();
|
||||
}
|
||||
else {
|
||||
roots.push_back(std::move(node));
|
||||
grouped_nodes[node.name] = &roots.back();
|
||||
}
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
void print_tree (const Node& tree, std::string_view desc={}, std::string indent={}) {
|
||||
std::cout << indent << desc << " (\"" << tree.name << "\")";
|
||||
if (tree.info and tree.info->deleted)
|
||||
std::cout << " DELETED";
|
||||
std::cout << '\n';
|
||||
|
||||
for (const auto& child : tree.children) {
|
||||
print_tree(child, child.info->visible_name, indent + "\t");
|
||||
}
|
||||
}
|
||||
|
||||
std::uintmax_t recursive_size (const Node& tree) {
|
||||
std::uintmax_t retval = 0;
|
||||
if (tree.info)
|
||||
retval = tree.info->total_size;
|
||||
|
||||
for (const auto& child : tree.children) {
|
||||
retval += recursive_size(child);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::vector<const NotebookInfo*> to_flat_notebook_list (const Node& tree) {
|
||||
std::vector<const NotebookInfo*> retval;
|
||||
for (const auto& child : tree.children) {
|
||||
retval += to_flat_notebook_list(child);
|
||||
}
|
||||
if (tree.info)
|
||||
retval.push_back(tree.info);
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::vector<const NotebookInfo*> make_dele_list (const Node& tree) {
|
||||
if (tree.info and tree.info->deleted)
|
||||
return to_flat_notebook_list(tree);
|
||||
|
||||
std::vector<const NotebookInfo*> retval;
|
||||
for (const auto& child : tree.children) {
|
||||
retval += make_dele_list(child);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
int main() {
|
||||
using std::string_view;
|
||||
|
||||
rmlab::Notebook doc(BASE_PATH "a4bcfe83-a91d-485b-b27c-2a06aa287641");
|
||||
//rmlab::Notebook doc(BASE_PATH "a4bcfe83-a91d-485b-b27c-2a06aa287641");
|
||||
//rmlab::Notebook doc(BASE_PATH "aa90b0e7-5c1a-42fe-930f-dad9cf3363cc");
|
||||
|
||||
std::cout << "Document version: " << doc.version << '\n';
|
||||
//std::cout << "Document version: " << doc.version << '\n';
|
||||
|
||||
auto notebooks = build_notebook_infos(BASE_PATH);
|
||||
|
||||
int z = 0;
|
||||
std::uintmax_t total = 0;
|
||||
for (auto& nb : notebooks) {
|
||||
std::cout << nb << '\n';
|
||||
std::cout << "------ " << z << "-----\n";
|
||||
++z;
|
||||
total += nb.second.total_size;
|
||||
//naive linear delete
|
||||
//std::uintmax_t total = 0;
|
||||
//std::uintmax_t freed = 0;
|
||||
//std::set<std::string_view> dele_list;
|
||||
//for (auto& nb : notebooks) {
|
||||
// if (nb.second.deleted) {
|
||||
// dele_list.insert(nb.first);
|
||||
// for (const auto& file : nb.second.files) {
|
||||
// std::cout << BASE_PATH << file << '\n';
|
||||
// }
|
||||
// freed += nb.second.total_size;
|
||||
// }
|
||||
// total += nb.second.total_size;
|
||||
//}
|
||||
|
||||
//for (auto& nb : notebooks) {
|
||||
// if (dele_list.count(nb.second.parent) and not nb.second.deleted) {
|
||||
// std::cerr << "Warning: " << nb.second.parent << " deleted but it's the parent of " << nb.first << "!\n";
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//std::cout << "Found " << notebooks.size() << " notebooks\n";
|
||||
//std::cout << "Total size " << std::setprecision(2) <<
|
||||
// static_cast<double>(total) / (1024.0 * 1024.0 * 1024.0) << "GiB\n";
|
||||
//std::cout << "Would delete " << dele_list.size() << " notebooks\n";
|
||||
//std::cout << "Would free " << std::setprecision(2) <<
|
||||
// static_cast<double>(freed) / (1024.0 * 1024.0 * 1024.0) << "GiB\n";
|
||||
|
||||
std::vector<Node> roots = to_tree(notebooks);
|
||||
//std::cout << "Found " << notebooks.size() << " notebooks\n";
|
||||
//std::cout << "Collection has " << roots.size() << " roots\n";
|
||||
//std::cout << " ---------\n";
|
||||
//for (const auto& root : roots) {
|
||||
// std::cout << "\tTree \"" << root.name << "\" has " << root.children.size() << " nodes\n\n";
|
||||
//}
|
||||
|
||||
//std::uintmax_t total_mem1 = 0;
|
||||
std::vector<const NotebookInfo*> dele_list;
|
||||
for (const auto& root : roots) {
|
||||
//std::string_view desc;
|
||||
//if (root.name == "trash")
|
||||
// desc = "rubbish bin";
|
||||
//else if (root.name == "")
|
||||
// desc = "root";
|
||||
|
||||
//print_tree(root, desc);
|
||||
//total_mem1 += recursive_size(root);
|
||||
dele_list += make_dele_list(root);
|
||||
}
|
||||
|
||||
std::cout << "Found " << notebooks.size() << " notebooks\n";
|
||||
std::cout << "Total size " << std::setprecision(2) <<
|
||||
static_cast<double>(total) / (1024.0 * 1024.0 * 1024.0) << "GiB\n";
|
||||
//std::cout << "Total size 1: " << total_mem1 << " bytes\n";
|
||||
//const std::uintmax_t total_mem2 = std::accumulate(notebooks.cbegin(), notebooks.cend(), std::uintmax_t{}, [](std::uintmax_t a, const auto& b) {return a + b.second.total_size;});
|
||||
//std::cout << "Total size 2: " << total_mem2 << " bytes\n";
|
||||
|
||||
for (const NotebookInfo* nb : dele_list) {
|
||||
for (const auto& f : nb->files) {
|
||||
std::cout << "/home/root/.local/share/remarkable/xochitl/" << f << '\n';
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue