Skip to content

Commit

Permalink
Use structured bindings to unpack line, use std::filesystem::path ins…
Browse files Browse the repository at this point in the history
…tead of strings for iflepaths, fully remove pathmaster dependency, simplify error handling.
  • Loading branch information
ryouze committed Sep 24, 2024
1 parent 576fc4f commit c0f2f35
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 80 deletions.
7 changes: 5 additions & 2 deletions src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "app.hpp"
#include "core/args.hpp"
#include "core/string.hpp"
#include "modules/analyze.hpp"

void app::run(const int argc,
Expand All @@ -15,14 +16,16 @@ void app::run(const int argc,
// Process command-line arguments (this might throw an ArgParseError)
const core::args::Args args(argc, argv);

fmt::print("Analyzing {} files: [{}]\n\n", args.filepaths.size(), fmt::join(args.filepaths, ", "));
fmt::print("Analyzing {} files: [{}]\n\n",
args.filepaths.size(),
fmt::join(core::string::paths_to_strings(args.filepaths), ", "));
// fmt::print("Enabled: bare={}, unused={}, unlisted={}\n\n", args.enable.bare, args.enable.unused, args.enable.unlisted);

fmt::print("--------------------------------------------------------------------------------\n\n");

// Process each filepath
for (const auto &path : args.filepaths) {
fmt::print("##- {} -##\n\n", path);
fmt::print("##- {} -##\n\n", path.string());
const modules::analyze::CodeParser parser(path);

// Get references to the parser's extracted data / results
Expand Down
4 changes: 2 additions & 2 deletions src/core/args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,15 @@ core::args::Args::Args(const int argc,
}
// Append only if the file extension matches any of the C++ file types
if (file_extensions.find(entry.path().extension().string()) != file_extensions.cend()) {
this->filepaths.emplace_back(entry.path().string());
this->filepaths.emplace_back(entry.path());
}
}
}
// Otherwise, use the file path directly
else {
// Append only if the file extension matches any of the C++ file types
if (file_extensions.find(resolved_filepath.extension().string()) != file_extensions.cend()) {
this->filepaths.emplace_back(resolved_filepath.string());
this->filepaths.emplace_back(resolved_filepath);
}
}
}
Expand Down
16 changes: 12 additions & 4 deletions src/core/args.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,25 @@

#pragma once

#include <stdexcept> // for std::runtime_error
#include <string> // for std::string
#include <vector> // for std::vector
#include <filesystem> // for std::filesystem
#include <stdexcept> // for std::runtime_error
#include <string> // for std::string
#include <vector> // for std::vector

namespace core::args {

/**
* @brief Exceptions raised by command-line argument parser when help or version is requested. The requested message is returned.
*
* This class extends "std::runtime_error".
*/
class ArgsError : public std::runtime_error {
public:
/**
* @brief Construct a new ArgsError object.
*
* @param message Error message that describes the cause of the exception (e.g., "Failed to load data").
*/
explicit ArgsError(const std::string &message)
: std::runtime_error(message) {}
};
Expand Down Expand Up @@ -68,7 +76,7 @@ class Args final {
/**
* @brief Vector of file paths.
*/
std::vector<std::string> filepaths;
std::vector<std::filesystem::path> filepaths;

/**
* @brief Struct of enabled features (e.g., "Enable(false, true, true)").
Expand Down
36 changes: 28 additions & 8 deletions src/core/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,38 @@
* @file io.cpp
*/

#include <cstddef> // for std::size_t
#include <exception> // for std::exception
#include <fstream> // for std::ifstream
#include <stdexcept> // for std::runtime_error
#include <string> // for std::string, std::getline
#include <vector> // for std::vector
#include <cstddef> // for std::size_t
#include <exception> // for std::exception
#include <filesystem> // for std::filesystem
#include <fstream> // for std::ifstream
#include <stdexcept> // for std::runtime_error
#include <string> // for std::string, std::getline
#include <vector> // for std::vector

#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <locale> // for setlocale, LC_ALL
#include <windows.h> // for WideCharToMultiByte, GetLastError, CP_UTF8, SetConsoleCP, SetConsoleOutputCP
#endif

#include <fmt/core.h>

#include "io.hpp"

std::vector<core::io::Line> core::io::read_lines(const std::string &input_path,
void core::io::setup_utf8_console()
{
#if defined(_WIN32)
if (!SetConsoleCP(CP_UTF8) || !SetConsoleOutputCP(CP_UTF8)) {
throw std::runtime_error(fmt::format("Failed to set UTF-8 code page: {}", GetLastError()));
}

if (!setlocale(LC_ALL, ".UTF8")) {
throw std::runtime_error("Failed to set UTF-8 locale");
}
#endif
}

std::vector<core::io::Line> core::io::read_lines(const std::filesystem::path &input_path,
const std::size_t initial_capacity)
{
try {
Expand Down Expand Up @@ -44,6 +64,6 @@ std::vector<core::io::Line> core::io::read_lines(const std::string &input_path,
return lines;
}
catch (const std::exception &e) {
throw IOError(fmt::format("Error loading file '{}': {}", input_path, e.what()));
throw std::runtime_error(fmt::format("Error loading file '{}': {}", input_path.string(), e.what()));
}
}
25 changes: 12 additions & 13 deletions src/core/io.hpp
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
/**
* @file io.hpp
*
* @brief Load lines of text from disk.
* @brief Input/output functions.
*/

#pragma once

#include <cstddef> // for std::size_t
#include <stdexcept> // for std::runtime_error
#include <string> // for std::string
#include <vector> // for std::vector
#include <cstddef> // for std::size_t
#include <filesystem> // for std::filesystem
#include <stdexcept> // for std::runtime_error
#include <string> // for std::string
#include <vector> // for std::vector

namespace core::io {

/**
* @brief Base class for exceptions raised during I/O operations.
* @brief Setup UTF-8 input/output on Windows. Do nothing on other platforms.
*
* @throws std::runtime_error If failed to enable UTF-8 encoding on Windows.
*/
class IOError : public std::runtime_error {
public:
explicit IOError(const std::string &message)
: std::runtime_error(message) {}
};
void setup_utf8_console();

/**
* @brief Struct that represents a single line of text.
Expand Down Expand Up @@ -58,9 +57,9 @@ struct Line {
*
* @return Vector of Line structs (e.g., {Line(1, "Hello world!"), Line(2, "How are you?")}).
*
* @throws core::io::IOError If the file cannot be opened for reading or if any other I/O error occurs.
* @throws std::runtime_error If the file cannot be opened for reading or if any other I/O error occurs.
*/
[[nodiscard]] std::vector<Line> read_lines(const std::string &input_path,
[[nodiscard]] std::vector<Line> read_lines(const std::filesystem::path &input_path,
const std::size_t initial_capacity = 100);

} // namespace core::io
19 changes: 19 additions & 0 deletions src/core/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,31 @@
#include <algorithm> // for std::transform, std::find_if_not
#include <cctype> // for std::tolower, std::isspace
#include <cstddef> // for std::size_t
#include <filesystem> // for std::filesystem
#include <sstream> // for std::ostringstream
#include <string> // for std::string
#include <unordered_map> // for std::unordered_map
#include <vector> // for std::vector

#include "string.hpp"

std::vector<std::string> core::string::paths_to_strings(const std::vector<std::filesystem::path> &paths)
{
// Get the length of the original vector
const std::size_t paths_len = paths.size();

// Convert vector to vector of strings
std::vector<std::string> strings;
strings.reserve(paths_len);
for (const auto &path : paths) {
strings.emplace_back(path.string());
}

// Return shrunk vector (RVO)
strings.shrink_to_fit();
return strings;
}

std::string core::string::to_lower(std::string str)
{
std::transform(str.cbegin(), str.cend(), str.begin(),
Expand Down
12 changes: 11 additions & 1 deletion src/core/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,20 @@

#pragma once

#include <string> // for std::string
#include <filesystem> // for std::filesystem
#include <string> // for std::string
#include <vector> // for std::vector

namespace core::string {

/**
* @brief Convert a vector of filesystem paths to a vector of strings.
*
* @param paths Vector of filesystem paths (e.g., {"/path/to/file1", "/path/to/file2"}).
* @return Vector of strings (e.g., {"/path/to/file1", "/path/to/file2"}).
*/
[[nodiscard]] std::vector<std::string> paths_to_strings(const std::vector<std::filesystem::path> &v);

/**
* @brief Convert a string to lowercase.
*
Expand Down
23 changes: 3 additions & 20 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@

#include <fmt/core.h>

#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <locale> // for setlocale, LC_ALL
#include <windows.h> // for WideCharToMultiByte, GetLastError, CP_UTF8, SetConsoleCP, SetConsoleOutputCP
#endif

#include "app.hpp"
#include "core/args.hpp"
#include "core/io.hpp"

/**
* @brief Entry-point of the application.
Expand All @@ -28,20 +23,8 @@ int main(int argc,
char **argv)
{
try {

#if defined(_WIN32)
if (!SetConsoleCP(CP_UTF8) || !SetConsoleOutputCP(CP_UTF8)) {
if (throw_on_error) {
throw PathmasterError("Failed to set UTF-8 code page on Windows: " + std::to_string(GetLastError()));
}
}

if (!setlocale(LC_ALL, ".UTF8")) {
if (throw_on_error) {
throw PathmasterError("Failed to set UTF-8 locale on Windows");
}
}
#endif
// Setup UTF-8 input/output on Windows
core::io::setup_utf8_console();

// Run the application
app::run(argc, argv);
Expand Down
15 changes: 8 additions & 7 deletions src/modules/analyze.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

#include <algorithm> // for std::transform, std::find
#include <filesystem> // for std::filesystem
#include <iterator> // for std::back_inserter
#include <regex> // for std::regex, std::smatch, std::sregex_iterator, std::regex_search
#include <string> // for std::string
Expand Down Expand Up @@ -39,7 +40,7 @@ namespace {

} // namespace

modules::analyze::CodeParser::CodeParser(const std::string &input_path)
modules::analyze::CodeParser::CodeParser(const std::filesystem::path &input_path)
{
// Define the regex for an include directive (e.g., "#include <iostream>") and a function call (e.g., "std::cout")
static const std::regex include_directive_regex(R"(^\s*#include\s*<\S+>)", std::regex::optimize);
Expand All @@ -50,15 +51,15 @@ modules::analyze::CodeParser::CodeParser(const std::string &input_path)
std::vector<modules::analyze::UnlistedFunction> temp_functions;

// Load the file from disk and iterate over each line
for (const core::io::Line &current_line : core::io::read_lines(input_path)) {
for (const auto &[line_number, line_text] : core::io::read_lines(input_path)) {

// Skip if the raw line is empty (avoid stripping and lowercasing the line)
if (current_line.text.empty()) {
if (line_text.empty()) {
continue;
}

// Strip leading and trailing whitespace from line, turn line lowercase
std::string processed_line = core::string::to_lower(core::string::strip_whitespace(current_line.text));
std::string processed_line = core::string::to_lower(core::string::strip_whitespace(line_text));

// Skip if the processed line is a comment
if (begins_with_comment(processed_line)) {
Expand Down Expand Up @@ -88,17 +89,17 @@ modules::analyze::CodeParser::CodeParser(const std::string &input_path)
// Categorize the result into containers
if (line_contains_include && !function_calls.empty()) {
// E.g., "#include <iostream> // for std::cout, std::cerr"
temp_includes_with_functions.emplace_back(current_line.number, current_line.text, function_calls);
temp_includes_with_functions.emplace_back(line_number, line_text, function_calls);
}
else if (line_contains_include) {
// E.g., "#include <string>""
this->bare_includes_.emplace_back(current_line.number, current_line.text, include_directive);
this->bare_includes_.emplace_back(line_number, line_text, include_directive);
}
else if (!function_calls.empty()) {
// E.g., "std::string"
for (const auto &function_name : function_calls) {
// Do not create C++ reference links, we do this later
temp_functions.emplace_back(current_line.number, current_line.text, function_name, "");
temp_functions.emplace_back(line_number, line_text, function_name, "");
}
}
// Otherwise, the line is ignored, e.g., '#include "my_header.hpp"'
Expand Down
9 changes: 5 additions & 4 deletions src/modules/analyze.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

#pragma once

#include <cstddef> // for std::size_t
#include <string> // for std::string
#include <vector> // for std::vector
#include <cstddef> // for std::size_t
#include <filesystem> // for std::filesystem
#include <string> // for std::string
#include <vector> // for std::vector

#include "core/io.hpp"

Expand Down Expand Up @@ -132,7 +133,7 @@ class CodeParser final {
*
* @param input_path Path to the C++ file that shall be parsed (e.g., "~/main.cpp").
*/
explicit CodeParser(const std::string &input_path);
explicit CodeParser(const std::filesystem::path &input_path);

/**
* @brief Get a vector of bare include directives, i.e., without any standard functions listed after them as comments.
Expand Down
Loading

0 comments on commit c0f2f35

Please sign in to comment.