vault class

This commit is contained in:
2025-11-02 17:53:29 +01:00
parent f5f6db48b2
commit 3922456171
8 changed files with 228 additions and 88 deletions

View File

@@ -1 +1 @@
Checks: '*,clang-analyzer-*,-llvmlibc-*,-fuchsia-*,-altera-*,-abseil-*,-android-*,-modernize-use-trailing-return-type,-readability-identifier-length,-*-readability-todo,-*-magic-numbers,-readability-function-cognitive-complexity,-*-easily-swappable-parameters,-*-pro-type-reinterpret-cast,-*-owning-memory' Checks: '*,clang-analyzer-*,-llvmlibc-*,-fuchsia-*,-altera-*,-abseil-*,-android-*,-modernize-use-trailing-return-type,-readability-identifier-length,-*-readability-todo,-*-magic-numbers,-readability-function-cognitive-complexity,-*-easily-swappable-parameters,-*-pro-type-reinterpret-cast,-*-owning-memory,-concurrency-mt-unsafe,-cert-env33-c'

View File

@@ -12,7 +12,7 @@ set(CMAKE_AUTOUIC ON)
qt6_wrap_ui(UI_HEADERS src/mainwindow.ui) qt6_wrap_ui(UI_HEADERS src/mainwindow.ui)
add_executable(${PROJECT_NAME} src/main.cc src/mainwindow.cc ${UI_HEADERS}) add_executable(${PROJECT_NAME} src/main.cc src/mainwindow.cc src/vault.cc ${UI_HEADERS})
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <string>
#define ASSERT(cond) \ #define ASSERT(cond) \
if (!(cond)) { \ if (!(cond)) { \
@@ -19,4 +20,9 @@ using u64 = uint64_t;
static_assert(sizeof(float) * 8 == 32); static_assert(sizeof(float) * 8 == 32);
using f32 = float; using f32 = float;
static_assert(sizeof(double) * 8 == 64); static_assert(sizeof(double) * 8 == 64);
using f64 = double; using f64 = double;
inline std::string path_to_filename(const std::string &path) {
u64 pos = path.find_last_of("/\\");
return (pos == std::string::npos) ? path : path.substr(pos + 1);
}

View File

@@ -1,9 +1,8 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "vault.h"
#include <QDebug>
#include <QFileDialog> #include <QFileDialog>
#include <QTreeWidgetItem> #include <QInputDialog>
#include <fstream> #include <QTemporaryFile>
#include <iostream>
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) { : QMainWindow(parent), ui(new Ui::MainWindow) {
@@ -17,16 +16,8 @@ MainWindow::MainWindow(QWidget *parent)
return; return;
} }
{ m_vault = Vault(path.toStdString());
std::ofstream file(path.toStdString(), std::ios::binary); reload_fs_tree();
Vault::write_header(file);
Vault::write_file(file, "hello.txt", "Hello, World!");
Vault::write_file(file, "test.txt", "test test test");
}
m_vault_path = path.toStdString();
reload_vault();
}); });
connect(ui->actionOpen, &QAction::triggered, this, [this]() { connect(ui->actionOpen, &QAction::triggered, this, [this]() {
@@ -37,52 +28,105 @@ MainWindow::MainWindow(QWidget *parent)
return; return;
} }
m_vault_path = path.toStdString(); m_vault = Vault(path.toStdString());
reload_vault(); reload_fs_tree();
}); });
connect(ui->fsTreeWidget, &QTreeWidget::itemClicked, connect(ui->fsTreeWidget, &QTreeWidget::itemClicked,
[this](QTreeWidgetItem *item, int column) { [this](QTreeWidgetItem *item, int column) {
preview_file(item->text(column).toStdString()); preview_file(item->text(column).toStdString());
}); });
connect(ui->fsTreeWidget, &QTreeWidget::customContextMenuRequested, this,
&MainWindow::file_context_menu);
connect(ui->actionAddFile, &QAction::triggered, this, [this]() {
if (!m_vault) {
return;
}
QString path = QFileDialog::getOpenFileName(this, "Choose a file to add");
if (path.isEmpty()) {
return;
}
std::ifstream file(path.toStdString(), std::ios::binary);
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
m_vault->write_file(path_to_filename(path.toStdString()), content);
reload_fs_tree();
});
connect(ui->actionCreateFile, &QAction::triggered, this, [this]() {
if (!m_vault) {
return;
}
QString path =
QInputDialog::getText(this, "Create File", "Where to create the file");
if (path.isEmpty()) {
return;
}
m_vault->write_file(path.toStdString(), "");
reload_fs_tree();
});
} }
void MainWindow::reload_vault() { void MainWindow::reload_fs_tree() {
ui->fsTreeWidget->clear(); ui->fsTreeWidget->clear();
std::ifstream file(m_vault_path, std::ios::binary); auto headers = m_vault->read_file_headers();
Vault::verify_header(file); for (const auto &header : headers) {
while (true) {
auto header = Vault::read_file_header(file);
if (!header) {
break;
}
file.seekg(static_cast<i64>(header->size), std::ios::cur);
auto *item = new QTreeWidgetItem(ui->fsTreeWidget); auto *item = new QTreeWidgetItem(ui->fsTreeWidget);
item->setText(0, QString::fromStdString(header->name)); item->setText(0, QString::fromStdString(header.name));
} }
} }
void MainWindow::preview_file(const std::string &filename) { void MainWindow::preview_file(const std::string &filename) {
std::ifstream file(m_vault_path, std::ios::binary); auto content = m_vault->read_file(filename);
Vault::verify_header(file); if (content) {
ui->filePreview->setText(QString::fromStdString(content.value()));
} else {
qWarning() << "File to preview not found";
}
}
while (true) { void MainWindow::edit_file(const std::string &filename) {
auto header = Vault::read_file_header(file); auto content = m_vault->read_file(filename);
if (!header) { if (content) {
break; QTemporaryFile temp_file;
ASSERT(temp_file.open());
{
QTextStream out(&temp_file);
out << QString::fromStdString(content.value());
} }
temp_file.flush();
if (header->name == filename) { // TODO: xdg-open or something
std::string content(header->size, '\0'); std::system(("kwrite " + temp_file.fileName()).toStdString().c_str());
file.read(content.data(), static_cast<i64>(header->size)); // TODO: write back
ui->filePreview->setText(QString::fromStdString(content)); } else {
return; qWarning() << "File to edit not found";
} }
file.seekg(static_cast<i64>(header->size), std::ios::cur); }
void MainWindow::file_context_menu(const QPoint &pos) {
QTreeWidgetItem *item = ui->fsTreeWidget->itemAt(pos);
if (item == nullptr) {
return;
} }
ASSERT(false); QMenu menu(this);
QAction *edit_action = menu.addAction(
style()->standardIcon(QStyle::SP_FileDialogDetailedView), "Edit");
connect(edit_action, &QAction::triggered, this,
[this, item]() { edit_file(item->text(0).toStdString()); });
menu.exec(ui->fsTreeWidget->mapToGlobal(pos));
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "vault.h"
#include <QMainWindow> #include <QMainWindow>
class MainWindow : public QMainWindow { class MainWindow : public QMainWindow {
@@ -12,8 +13,10 @@ public:
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
std::string m_vault_path; std::optional<Vault> m_vault;
void reload_vault(); void reload_fs_tree();
void preview_file(const std::string &filename); void preview_file(const std::string &filename);
void edit_file(const std::string &filename);
void file_context_menu(const QPoint &pos);
}; };

View File

@@ -11,12 +11,15 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>MainWindow</string> <string>dull</string>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QTreeWidget" name="fsTreeWidget"> <widget class="QTreeWidget" name="fsTreeWidget">
<property name="contextMenuPolicy">
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
</property>
<column> <column>
<property name="text"> <property name="text">
<string>Name</string> <string>Name</string>
@@ -25,8 +28,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QTextBrowser" name="filePreview"> <widget class="QTextBrowser" name="filePreview"/>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
@@ -46,7 +48,15 @@
<addaction name="actionNew"/> <addaction name="actionNew"/>
<addaction name="actionOpen"/> <addaction name="actionOpen"/>
</widget> </widget>
<widget class="QMenu" name="menuFiles">
<property name="title">
<string>Files</string>
</property>
<addaction name="actionCreateFile"/>
<addaction name="actionAddFile"/>
</widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuFiles"/>
</widget> </widget>
<action name="actionNew"> <action name="actionNew">
<property name="text"> <property name="text">
@@ -58,6 +68,16 @@
<string>Open</string> <string>Open</string>
</property> </property>
</action> </action>
<action name="actionAddFile">
<property name="text">
<string>Add</string>
</property>
</action>
<action name="actionCreateFile">
<property name="text">
<string>Create</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

92
src/vault.cc Normal file
View File

@@ -0,0 +1,92 @@
#include "vault.h"
#include "common.h"
#include <array>
#include <iostream>
Vault::Vault(std::string path) : m_path(std::move(path)) {
m_file.open(m_path, std::ios::in | std::ios::out | std::ios::binary);
if (!m_file.is_open()) {
std::ofstream create(m_path, std::ios::binary);
create.write("DULL", 4);
create.write(reinterpret_cast<const char *>(&VERSION), sizeof(VERSION));
create.close();
m_file.open(m_path, std::ios::in | std::ios::out | std::ios::binary);
}
ASSERT(m_file.good());
std::array<char, 4> header{};
ASSERT(static_cast<bool>(m_file.read(header.data(), header.size())));
ASSERT(std::string_view(header.data(), header.size()) == "DULL");
std::int16_t version = 0;
ASSERT(m_file.read(reinterpret_cast<char *>(&version), sizeof(version)));
ASSERT(version == VERSION);
}
std::vector<FileHeader> Vault::read_file_headers() {
std::vector<FileHeader> headers;
m_file.clear();
m_file.seekg(AFTER_HEADER_OFFSET, std::ios::beg);
while (true) {
u64 name_size = 0;
if (!m_file.read(reinterpret_cast<char *>(&name_size), sizeof(u64))) {
break;
}
FileHeader header{};
header.name.resize(name_size);
m_file.read(header.name.data(), static_cast<i64>(name_size));
m_file.read(reinterpret_cast<char *>(&header.size), sizeof(u64));
m_file.seekg(static_cast<i64>(header.size), std::ios::cur);
headers.push_back(header);
}
return headers;
}
std::optional<std::string> Vault::read_file(const std::string &filename) {
m_file.clear();
m_file.seekg(AFTER_HEADER_OFFSET, std::ios::beg);
while (true) {
u64 name_size = 0;
if (!m_file.read(reinterpret_cast<char *>(&name_size), sizeof(u64))) {
break;
}
std::string name;
name.resize(name_size);
m_file.read(name.data(), static_cast<i64>(name_size));
u64 content_size = 0;
m_file.read(reinterpret_cast<char *>(&content_size), sizeof(u64));
if (name == filename) {
std::string content(content_size, '\0');
m_file.read(content.data(), static_cast<i64>(content_size));
return content;
}
m_file.seekg(static_cast<i64>(content_size), std::ios::cur);
}
return std::nullopt;
}
void Vault::write_file(const std::string &name, const std::string &content) {
m_file.clear();
m_file.seekp(0, std::ios::end);
u64 name_size = name.size();
u64 content_size = content.size();
m_file.write(reinterpret_cast<const char *>(&name_size), sizeof(u64));
m_file.write(name.data(), static_cast<i64>(name_size));
m_file.write(reinterpret_cast<const char *>(&content_size), sizeof(u64));
m_file.write(content.data(), static_cast<i64>(content_size));
m_file.flush();
}

View File

@@ -1,52 +1,27 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
#include <array>
#include <fstream> #include <fstream>
#include <iostream>
#include <optional> #include <optional>
#include <vector>
namespace Vault { constexpr i16 VERSION = 1;
constexpr u64 AFTER_HEADER_OFFSET = 6;
inline void verify_header(std::ifstream &file) {
ASSERT(file.good());
std::array<char, 4> header{};
ASSERT(file.read(header.data(), header.size()));
ASSERT(std::string_view(header.data(), header.size()) == "DULL");
}
struct FileHeader { struct FileHeader {
std::string name; std::string name;
u64 size; u64 size;
}; };
inline std::optional<FileHeader> read_file_header(std::ifstream &file) { class Vault {
FileHeader header{}; public:
explicit Vault(std::string path);
u64 name_size = 0; std::vector<FileHeader> read_file_headers();
if (!file.read(reinterpret_cast<char *>(&name_size), sizeof(u64))) { std::optional<std::string> read_file(const std::string &name);
return std::nullopt; void write_file(const std::string &name, const std::string &content);
}
header.name = std::string(name_size, '\0'); private:
file.read(header.name.data(), static_cast<i64>(name_size)); std::string m_path;
std::fstream m_file;
file.read(reinterpret_cast<char *>(&header.size), sizeof(u64)); };
return header;
}
inline void write_header(std::ofstream &file) { file.write("DULL", 4); }
inline void write_file(std::ofstream &file, const std::string &name,
const std::string &content) {
u64 name_size = name.size();
u64 content_size = content.size();
file.write(reinterpret_cast<const char *>(&name_size), sizeof(u64));
file.write(name.data(), static_cast<i64>(name_size));
file.write(reinterpret_cast<const char *>(&content_size), sizeof(u64));
file.write(content.data(), static_cast<i64>(content_size));
}
}; // namespace Vault