diff --git a/filesystem/src/fs.cpp b/filesystem/src/fs.cpp index 8e328e4..8a28623 100644 --- a/filesystem/src/fs.cpp +++ b/filesystem/src/fs.cpp @@ -17,9 +17,8 @@ extern "C" { #include #include -#include "query_lang/parser.hpp" -#include "fs.hpp" #include "db.hpp" +#include "fs.hpp" #include "command_interface.h" #include "utilities.hpp" @@ -41,15 +40,20 @@ int lake_getattr(const char *path, struct stat *stbuf) auto file = reverse_query(path); - if (file.empty()) { + if (!file.has_value()) + { + return file.error(); + } + + if (file.value().empty()) { spdlog::error("No file found!"); return -ENOENT; } - spdlog::debug("stat'ing file at {0}", file); + spdlog::debug("stat'ing file at {0}", file.value()); - if (stat(file.c_str(), stbuf) < 0) + if (stat(file.value().c_str(), stbuf) < 0) { spdlog::error("Error stat'ing: {0}", strerror(errno)); @@ -82,36 +86,14 @@ int lake_readdir( filler(buf, "..", nullptr, 0); #endif - std::vector files; - - // TODO: very nested, smelly - - // Check if path is a query - if (path[strlen(path) - 1] == ')') { - std::string query = extract_query(path); - - try { - const auto query_ast = parse(query); - - if (query_ast.has_value()) { - files = db_run_query(query_ast.value()); - - } else { - spdlog::error("Error while parsing, invalid query"); + const auto files = get_files(path); - return -EINVAL; - } - } catch (std::exception err) { - spdlog::error("Error while parsing: {0}", err.what()); - - return -EINVAL; - } - - } else { - files = db_run_default_query(); + if (!files.has_value()) + { + return files.error(); } - for (const auto& file : files) { + for (const auto& file : files.value()) { const std::string file_name = file.substr(file.find_last_of("/") + 1); spdlog::trace("Will show file {0} as {1}", file, file_name); @@ -136,12 +118,19 @@ int lake_open(const char *path, struct fuse_file_info *fi) { auto file_path = reverse_query(path); - if (file_path.empty()) + if (!file_path.has_value()) + { + return file_path.error(); + } + + if (file_path.value().empty()) + { return -ENOENT; + } - spdlog::trace("Found file {0} to open", file_path); + spdlog::trace("Found file {0} to open", file_path.value()); - fi->fh = open(file_path.c_str(), fi->flags); + fi->fh = open(file_path.value().c_str(), fi->flags); if (fi->fh == -1) { spdlog::error("Could not open file err: {0}", strerror(errno)); diff --git a/filesystem/src/utilities.cpp b/filesystem/src/utilities.cpp index a6f5071..ccf5862 100644 --- a/filesystem/src/utilities.cpp +++ b/filesystem/src/utilities.cpp @@ -1,64 +1,141 @@ // SPDX-FileCopyrightText: 2025 Caleb Depatie // // SPDX-License-Identifier: BSD-3-Clause +#include #include #include +#include #include "query_lang/parser.hpp" #include "db.hpp" #include "utilities.hpp" -std::string reverse_query(const char* path) { - auto path_s = std::string(path); +std::expected, int> get_files(const char* path) { + spdlog::trace("Entering get_files(path={0})", path); - spdlog::trace("Entering reverse_query(path={0})", path_s); + std::vector files; + + // Get path segments from query + const auto path_segments = split(path, "/"); + + const bool path_has_query = path_segments.size() != 0 && path_segments[0].contains('('); + + if (path_has_query) + { + const auto query = extract_query(path); + const auto query_ast = parse(query); + + if (query_ast.has_value()) { + files = db_run_query(query_ast.value()); + + } else { + spdlog::error("Error while parsing, invalid query"); + + return std::unexpected(-EINVAL); + } + } + else + { + files = db_run_default_query(); + } - std::vector query_files; + if (path_segments.size() > 1) + { - if (path_s.find_first_of('(') == std::string::npos) { - query_files = db_run_default_query(); + // Get the file path by comparing the file name to the query results + std::string file_path; + std::string looking_for_file; - } else { - std::string query = extract_query(path); + if (path_has_query) + { + looking_for_file = path_segments[1]; + } + else + { + looking_for_file = path_segments[0]; + } - try { - const auto query_ast = parse(query); - if (query_ast.has_value()) { - query_files = db_run_query(query_ast.value()); + for (const auto& query_file : files) { - } else { - spdlog::error("Error while parsing, no value returned"); + const std::string query_file_name = + query_file.substr(query_file.find_last_of("/") + 1); - return ""; + if (query_file_name == looking_for_file) { + file_path = query_file; + break; } - - } catch (std::exception err) { - spdlog::error("Error while parsing: {0}", err.what()); - - return ""; } - } - // Get the file path by comparing the file name to the query results - std::string file_path; + // Have to drill down arbitry path lengths + // Take current real path, get dir entries + // look for next folder, get that real path + // repeat + for (int i = 1 + path_has_query; i < path_segments.size(); i++) + { + auto finding_dir = path_segments[i]; + + const auto dir_iter = std::filesystem::directory_iterator(file_path); + + for (const auto& dir_entry : dir_iter) + { + if (dir_entry.path().filename() == finding_dir) + { + file_path = dir_entry.path(); + break; + } + } + } - const std::string looking_for_file = - std::string(path_s).substr(std::string(path_s).find_last_of("/") + 1); + if (std::filesystem::is_directory(file_path)) + { + // Query is for a folder + spdlog::debug("Found real dir path: {0}", file_path.c_str()); - for (const auto& query_file : query_files) { - - const std::string query_file_name = - query_file.substr(query_file.find_last_of("/") + 1); + // Get files of our actual destination folder + files.clear(); - if (query_file_name == looking_for_file) { - file_path = query_file; - break; + for (auto const& dir_entry : std::filesystem::directory_iterator{file_path}) + { + files.push_back(dir_entry.path()); + } } } - spdlog::trace("Exiting reverse_query() -> {0}", file_path); - return file_path; + spdlog::trace("Exiting get_files() -> vec.size()={0}", files.size()); + return files; +} + +std::expected reverse_query(const char* path) { + auto path_s = std::string(path); + + spdlog::trace("Entering reverse_query(path={0})", path_s); + + const auto path_segments = split(path, "/"); + + std::string looking_for_file = path_segments[path_segments.size()-1]; + + std::string reconstructed_path; + + for (int i = 0; i < path_segments.size()-1; i++) + { + reconstructed_path += "/" + path_segments[i]; + } + + const auto files = get_files(reconstructed_path.c_str()); + + if (!files.has_value()) + { + return std::unexpected(files.error()); + } + + const auto file_path = std::find_if(files->begin(), files->end(), + [looking_for_file] (const std::string& file_name) -> bool { + return std::filesystem::path(file_name).filename() == looking_for_file; + }); + + spdlog::trace("Exiting reverse_query() -> {0}", *file_path); + return *file_path; } std::string extract_query(const char* path) { @@ -70,6 +147,36 @@ std::string extract_query(const char* path) { const auto extracted_query = path_s.substr(path_s.find_first_of('('), path_s.find_last_of(')') - path_s.find_first_of('(') + 1); - spdlog::trace("Exiting parse() -> {0}", extracted_query); + spdlog::trace("Exiting extract_query() -> {0}", extracted_query); return extracted_query; +} + +std::vector split(const std::string str, const std::string delim) { + spdlog::trace("Entering split(str={0}, delim={1})", str, delim); + + std::vector parts; + + int part_start = 0; + for (int i = 0; i < str.size(); i++) + { + const std::string delim_match = str.substr(i, delim.size()); + + if (delim_match == delim) + { + if (part_start != i) + { + parts.push_back(str.substr(part_start, i-part_start)); + } + + part_start = i + delim.size(); + } + } + + if (part_start < str.size()) + { + parts.push_back(str.substr(part_start, str.size() - part_start)); + } + + spdlog::trace("Exiting split() -> vec.size()={0}", parts.size()); + return parts; } \ No newline at end of file diff --git a/filesystem/src/utilities.hpp b/filesystem/src/utilities.hpp index 8d386e8..530f9a9 100644 --- a/filesystem/src/utilities.hpp +++ b/filesystem/src/utilities.hpp @@ -4,9 +4,17 @@ #pragma once #include +#include +#include + +// Runs a query normally. Handles special logic to pull out folders +std::expected, int> get_files(const char* path); // Takes a query path that involves a tag query and returns the real FS path -std::string reverse_query(const char* path); +std::expected reverse_query(const char* path); // Takes a path and returns the query segment -std::string extract_query(const char* path); \ No newline at end of file +std::string extract_query(const char* path); + +// Split a string based on a delimeter +std::vector split(const std::string str, const std::string delim); \ No newline at end of file diff --git a/filesystem/tests/utilities.cpp b/filesystem/tests/utilities.cpp index 490a324..5887636 100644 --- a/filesystem/tests/utilities.cpp +++ b/filesystem/tests/utilities.cpp @@ -43,4 +43,28 @@ TEST_CASE("Query Extraction", "[utilities]") { CHECK(res == "(tag1 & ((!place) | (tag2 & tag3)))"); } -} \ No newline at end of file +} + +TEST_CASE("String Splitting", "[utilities]") { + SECTION("Single char delim") { + const auto haystack = "wow/this/is/a/path/"; + const auto needle = "/"; + + const auto found = split(haystack, needle); + + CHECK(found.size() == 5); + CHECK(found[0] == "wow"); + CHECK(found[4] == "path"); + } + + SECTION("Multi char delim") { + const auto haystack = ">