vault class
This commit is contained in:
@@ -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'
|
||||||
@@ -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})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#define ASSERT(cond) \
|
#define ASSERT(cond) \
|
||||||
if (!(cond)) { \
|
if (!(cond)) { \
|
||||||
@@ -20,3 +21,8 @@ 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);
|
||||||
|
}
|
||||||
@@ -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()));
|
||||||
while (true) {
|
} else {
|
||||||
auto header = Vault::read_file_header(file);
|
qWarning() << "File to preview not found";
|
||||||
if (!header) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (header->name == filename) {
|
void MainWindow::edit_file(const std::string &filename) {
|
||||||
std::string content(header->size, '\0');
|
auto content = m_vault->read_file(filename);
|
||||||
file.read(content.data(), static_cast<i64>(header->size));
|
if (content) {
|
||||||
ui->filePreview->setText(QString::fromStdString(content));
|
QTemporaryFile temp_file;
|
||||||
|
ASSERT(temp_file.open());
|
||||||
|
{
|
||||||
|
QTextStream out(&temp_file);
|
||||||
|
out << QString::fromStdString(content.value());
|
||||||
|
}
|
||||||
|
temp_file.flush();
|
||||||
|
|
||||||
|
// TODO: xdg-open or something
|
||||||
|
std::system(("kwrite " + temp_file.fileName()).toStdString().c_str());
|
||||||
|
// TODO: write back
|
||||||
|
} else {
|
||||||
|
qWarning() << "File to edit not found";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::file_context_menu(const QPoint &pos) {
|
||||||
|
QTreeWidgetItem *item = ui->fsTreeWidget->itemAt(pos);
|
||||||
|
if (item == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.seekg(static_cast<i64>(header->size), std::ios::cur);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
92
src/vault.cc
Normal 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();
|
||||||
|
}
|
||||||
51
src/vault.h
51
src/vault.h
@@ -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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user