logmeow/fragments/import.cpp

155 lines
5.1 KiB
C++

#include <imgui/imgui.h>
#include <nativefiledialog-extended/src/include/nfd.h>
#include <optional>
#include "../file.h"
#include "../logcat_entry.h"
static inline void import_logcat_entries(LogcatEntries& logcat_entries, bool ignore_buffer_lines, File& file);
static inline void import_logcat_entries_from_clipboard(LogcatEntries& logcat_entries, bool ignore_buffer_lines);
static void import_logcat_entries(LogcatEntries& logcat_entries, bool ignore_buffer_lines, auto cb);
static inline std::optional<File> open_file_picker();
void import_fragment(LogcatEntries& logcat_entries) {
static bool ignore_buffer_lines = false;
ImGui::Checkbox("Ignore buffer lines", &ignore_buffer_lines);
if (ImGui::Button("Import from File")) {
std::optional<File> file = open_file_picker();
if (file) {
ImGui::CloseCurrentPopup();
try {
import_logcat_entries(logcat_entries, ignore_buffer_lines, *file);
} catch (const std::exception& e) {
log(std::string("Failed to import logcat entries: ") + e.what());
}
}
}
ImGui::SameLine();
if (ImGui::Button("Import from Clipboard")) {
ImGui::CloseCurrentPopup();
try {
import_logcat_entries_from_clipboard(logcat_entries, ignore_buffer_lines);
} catch (const std::exception& e) {
log(std::string("Failed to import logcat entries: ") + e.what());
}
}
}
static inline void import_logcat_entries(LogcatEntries& logcat_entries, bool ignore_buffer_lines, File& file) {
auto handle_valid_char = [](char line[MAX_LOGCAT_LINE_SIZE], size_t* line_length, char c) -> std::optional<bool> {
if (c == '\n') {
return true;
}
if (*line_length == MAX_LOGCAT_LINE_SIZE) {
throw std::runtime_error("Received line longer than 512k");
}
line[*line_length] = c;
(*line_length)++;
return std::nullopt;
};
auto handle_char = [&](char line[MAX_LOGCAT_LINE_SIZE], size_t* line_length, int c) -> std::optional<bool> {
if (c != EOF) {
return handle_valid_char(line, line_length, static_cast<char>(c));
}
if (feof(file.get())) {
return *line_length != 0;
} else if (ferror(file.get())) {
throw_system_error("fread()");
} else {
throw std::runtime_error("fgetc() returned EOF without setting anything");
}
return std::nullopt;
};
import_logcat_entries(logcat_entries, ignore_buffer_lines, [&](char line[MAX_LOGCAT_LINE_SIZE], size_t* line_length) {
*line_length = 0;
while (true) {
int c = fgetc(file.get());
std::optional<bool> ret = handle_char(line, line_length, c);
if (ret) {
return *ret;
}
}
});
}
static inline void import_logcat_entries_from_clipboard(LogcatEntries& logcat_entries, bool ignore_buffer_lines) {
const char* text = ImGui::GetClipboardText();
import_logcat_entries(logcat_entries, ignore_buffer_lines, [&](char line[MAX_LOGCAT_LINE_SIZE], size_t* line_length) {
if (!text) {
return false;
}
const char* newline_offset = strchr(text, '\n');
*line_length = newline_offset ? static_cast<size_t>(newline_offset - text) : strlen(text);
if (*line_length > MAX_LOGCAT_LINE_SIZE) {
throw std::runtime_error("Received line longer than 512k");
}
memcpy(line, text, *line_length);
text = newline_offset ? &newline_offset[1] : nullptr;
return true;
});
}
static void import_logcat_entries(LogcatEntries& logcat_entries, bool ignore_buffer_lines, auto cb) {
char line[MAX_LOGCAT_LINE_SIZE];
size_t line_length;
Buffer buffer = Buffer::Unknown;
while (cb(line, &line_length)) {
if (line_length == 0) {
continue;
}
std::optional<LogcatEntry> logcat_entry = try_parse_logcat_entry(line, line_length, buffer);
if (logcat_entry) {
logcat_entries.push_back(std::move(*logcat_entry));
continue;
}
std::optional<Buffer> new_buffer = try_parse_buffer(line, line_length);
if (new_buffer) {
if (!ignore_buffer_lines) {
buffer = *new_buffer;
}
continue;
}
throw std::invalid_argument(std::string("Cannot parse line: ") + std::string(line, line_length));
}
}
static inline std::optional<File> open_file_picker() {
nfdchar_t* path;
nfdfilteritem_t filters[1] = {{"Log file", "log"}};
nfdresult_t res = NFD_OpenDialog(&path, filters, 1, nullptr);
if (res == NFD_OKAY) {
try {
File file(path, "r");
NFD_FreePath(path);
return file;
} catch (const std::exception& e) {
NFD_FreePath(path);
log(std::string("Failed to open file from file picker: ") + e.what());
}
} else if (res == NFD_CANCEL) {
// dialog was canceled, shrug
} else {
// ignore error when failing to open the file picker
}
return std::nullopt;
}