feat(directives): implement DirectiveValue class and IntStringDirective for enhanced directive handling and use std::variant instead of any

This commit is contained in:
whaffman 2025-09-25 21:57:02 +02:00
parent 1e80839864
commit 17b69afd2c
20 changed files with 192 additions and 54 deletions

View File

@ -163,7 +163,13 @@ void ServerConfig::parseDirectives(const std::string &declarations)
auto directive = DirectiveFactory::createDirective(line);
directives_.push_back(std::move(directive));
}
Log::info("Parsed " + std::to_string(directives_.size()) + " directives.", {{"declarations", declarations}});
Log::info("Parsed " + std::to_string(directives_.size()) + " directives.");
for (const auto &dir : directives_)
{
std::stringstream debugStream;
debugStream << "Directive parsed: " << *dir;
Log::debug(debugStream.str());
}
}
DirectiveValue ServerConfig::operator[](const std::string &directive) const

View File

@ -1,4 +1,4 @@
#include <webserv/config/directive/ADirective.hpp> // for ADirective
#include <webserv/config/directive/ADirective.hpp>
DirectiveValue ADirective::get() const
{
@ -13,4 +13,11 @@ std::string ADirective::getName() const
void ADirective::setName(const std::string &name)
{
name_ = name;
}
}
// Non-member stream operator implementations
std::ostream &operator<<(std::ostream &os, const DirectiveValue &dv)
{
return os << dv.toString();
}

View File

@ -1,18 +1,10 @@
#pragma once
#include <any>
#include <webserv/config/directive/DirectiveValue.hpp>
#include <iostream>
#include <string>
class DirectiveValue
{
public:
DirectiveValue(std::any value) : value_(value) {}
template <typename T> operator T() const { return std::any_cast<T>(value_); }
private:
std::any value_;
};
class ADirective
{
@ -26,36 +18,19 @@ class ADirective
ADirective(ADirective &&other) noexcept = delete;
ADirective &operator=(ADirective &&other) noexcept = delete;
virtual ~ADirective() {}
virtual void parse(const std::string &value) = 0;
[[nodiscard]] virtual std::any getValue() const = 0;
[[nodiscard]] DirectiveValue get() const;
[[nodiscard]] std::string getName() const;
virtual ~ADirective() = default;
void setName(const std::string &name);
virtual void parse(const std::string &value) = 0;
[[nodiscard]] virtual DirectiveValueType getValue() const = 0;
[[nodiscard]] DirectiveValue get() const;
[[nodiscard]] std::string getName() const;
// [[nodiscard]] std::string toString() const;
protected:
std::string name_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
std::string name_;
};
// Supported directives:
// - listen INT {server}
// - host STRING {server}
// - server_name STRING {server}
// - root STRING {server, location}
// - index STRING[] {server, location}
// - error_page INT STIRNG {server, location}
// - client_max_body_size SIZE {server, location}
// - autoindex BOOL {location}
// - allowed_methods STRING[] {location}
// - cgi_pass STRING {location}
// - cgi_ext STRING[] {location}
// - cgi_timout INT {location}
// - upload_enabled BOOL {location}
// - upload_store STRING {location}
// - redirect INT STRING {location}
// Non-member stream operator for ADirective
std::ostream &operator<<(std::ostream &os, const ADirective &directive);

View File

@ -24,7 +24,7 @@ void BoolDirective::parse(const std::string &arg)
}
}
std::any BoolDirective::getValue() const
DirectiveValueType BoolDirective::getValue() const
{
return value_;
}

View File

@ -20,7 +20,7 @@ class BoolDirective : public ADirective
void parse(const std::string &value) override;
[[nodiscard]] std::any getValue() const override;
[[nodiscard]] DirectiveValueType getValue() const override;
private:
bool value_ = false;

View File

@ -3,10 +3,14 @@
#include <webserv/config/directive/DirectiveFactory.hpp> // for DirectiveFactory
#include <webserv/config/directive/IntDirective.hpp>
#include <webserv/config/directive/SizeDirective.hpp>
#include <webserv/config/directive/IntStringDirective.hpp>
#include <webserv/config/directive/StringDirective.hpp>
#include <webserv/config/directive/VectorDirective.hpp>
#include <webserv/config/utils.hpp> // for trim, findCorrespondingClosingBrace, trimSemi
#include <webserv/log/Log.hpp> // for Log
#include <sstream>
std::unique_ptr<ADirective> DirectiveFactory::createDirective(const std::string &line)
{
@ -57,6 +61,8 @@ const std::unordered_map<std::string_view, DirectiveFactory::CreatorFunc> &Direc
[](const std::string &name, const std::string &arg) { return std::make_unique<SizeDirective>(name, arg); }},
{"StringDirective",
[](const std::string &name, const std::string &arg) { return std::make_unique<StringDirective>(name, arg); }},
{"IntStringDirective",
[](const std::string &name, const std::string &arg) { return std::make_unique<IntStringDirective>(name, arg); }},
{"VectorDirective",
[](const std::string &name, const std::string &arg) { return std::make_unique<VectorDirective>(name, arg); }},
};

View File

@ -29,7 +29,7 @@ class DirectiveFactory
{.name = "server_name", .type = "VectorDirective", .context = "SL"},
{.name = "root", .type = "StringDirective", .context = "SL"},
{.name = "index", .type = "VectorDirective", .context = "SL"},
{.name = "error_page", .type = "VectorDirective", .context = "SL"},
{.name = "error_page", .type = "IntStringDirective", .context = "SL"},
{.name = "client_max_body_size", .type = "SizeDirective", .context = "SL"},
{.name = "autoindex", .type = "BoolDirective", .context = "L"},
{.name = "allowed_methods", .type = "VectorDirective", .context = "L"},

View File

@ -0,0 +1,58 @@
#include <webserv/config/directive/ADirective.hpp>
#include <webserv/config/directive/DirectiveValue.hpp>
#include <cstddef>
#include <string>
#include <type_traits> // for is_same_v, decay_t
#include <utility> // for pair, move
#include <variant> // for variant, visit
#include <vector> // for vector
std::string DirectiveValue::toString() const
{
return std::visit(
[](const auto &val) -> std::string {
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, int> || std::is_same_v<T, size_t>)
{
return std::to_string(val);
}
else if constexpr (std::is_same_v<T, std::string>)
{
return val;
}
else if constexpr (std::is_same_v<T, bool>)
{
return val ? "true" : "false";
}
else if constexpr (std::is_same_v<T, std::vector<std::string>>)
{
std::string result = "[";
for (size_t i = 0; i < val.size(); ++i)
{
if (i > 0)
{
result += ", ";
}
result += "\"" + val[i] + "\"";
}
result += "]";
return result;
}
else if constexpr (std::is_same_v<T, std::pair<int, std::string>>)
{
return std::to_string(val.first) + " \"" + val.second + "\"";
}
else
{
return "<unknown>";
}
},
value_);
}
std::ostream &operator<<(std::ostream &os, const ADirective &directive)
{
return os << directive.getName() << ": " << directive.get().toString();
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <cstddef> // for size_t
#include <iostream> // for ostream, operator<<
#include <string> // for string, basic_string, char_traits, to_string
#include <variant> // for variant, visit, get, holds_alternative
#include <vector> // for vector
#include <utility> // for pair, move
// Define all possible directive value types
using DirectiveValueType = std::variant<int, // listen, error_page status, cgi_timeout
size_t,
bool, // autoindex, upload_enabled
std::string, // host, server_name, root, cgi_pass, upload_store
std::vector<std::string>, // index, allowed_methods, cgi_ext
std::pair<int, std::string> // error_page (status, path), redirect
>;
class DirectiveValue
{
public:
DirectiveValue(DirectiveValueType value) : value_(std::move(value)) {}
template <typename T> T get() const { return std::get<T>(value_); }
template <typename T> [[nodiscard]] bool holds() const { return std::holds_alternative<T>(value_); }
[[nodiscard]] std::string toString() const;
private:
DirectiveValueType value_;
};
// Non-member stream operator for DirectiveValue
std::ostream &operator<<(std::ostream &os, const DirectiveValue &dv);

View File

@ -7,7 +7,7 @@ void IntDirective::parse(const std::string &value)
value_ = std::stoi(value); // TODO: check parsing
}
std::any IntDirective::getValue() const
DirectiveValueType IntDirective::getValue() const
{
return value_;
}

View File

@ -20,7 +20,7 @@ class IntDirective : public ADirective
void parse(const std::string &value) override;
[[nodiscard]] std::any getValue() const override;
[[nodiscard]] DirectiveValueType getValue() const override;
private:
int value_ = 0;

View File

@ -0,0 +1,21 @@
#include "webserv/log/Log.hpp"
#include <webserv/config/directive/IntStringDirective.hpp> // for IntDirective
#include <webserv/config/utils.hpp> // for trim
#include <sstream> // for std::getline, std::basic_istream, std::char_traits, std::basic_stringbuf
void IntStringDirective::parse(const std::string &value)
{
std::istringstream iss(value);
int intPart = 0;
std::string strPart;
iss >> intPart;
std::getline(iss, strPart);
strPart = utils::trim(strPart); // Remove leading space
value_ = std::make_pair(intPart, strPart);
}
DirectiveValueType IntStringDirective::getValue() const
{
return value_;
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "ADirective.hpp"
#include <any>
class IntStringDirective : public ADirective
{
public:
IntStringDirective() = delete;
IntStringDirective(const std::string &name, const std::string &value) : ADirective(name) { parse(value); }
IntStringDirective(const IntStringDirective &other) = delete;
IntStringDirective &operator=(const IntStringDirective &other) = delete;
IntStringDirective(IntStringDirective &&other) noexcept = delete;
IntStringDirective &operator=(IntStringDirective &&other) noexcept = delete;
~IntStringDirective() override = default;
void parse(const std::string &value) override;
[[nodiscard]] DirectiveValueType getValue() const override;
private:
std::pair<int, std::string> value_ = {0, ""};
};

View File

@ -43,7 +43,7 @@ void SizeDirective::parse(const std::string &value)
value_ *= multiplier;
}
std::any SizeDirective::getValue() const
DirectiveValueType SizeDirective::getValue() const
{
return value_;
}

View File

@ -20,7 +20,7 @@ class SizeDirective : public ADirective
void parse(const std::string &value) override;
[[nodiscard]] std::any getValue() const override;
[[nodiscard]] DirectiveValueType getValue() const override;
private:
size_t value_ = 0;

View File

@ -7,7 +7,7 @@ void StringDirective::parse(const std::string &value)
value_ = value;
}
std::any StringDirective::getValue() const
DirectiveValueType StringDirective::getValue() const
{
return value_;
}

View File

@ -18,7 +18,7 @@ class StringDirective : public ADirective
void parse(const std::string &value) override;
[[nodiscard]] std::any getValue() const override;
[[nodiscard]] DirectiveValueType getValue() const override;
private:
std::string value_;

View File

@ -6,10 +6,10 @@
void VectorDirective::parse(const std::string &value)
{
std::stringstream ss(value);
while(ss.good())
while (ss.good())
{
std::string item;
std::getline(ss, item, ' ');// index indx.html
std::getline(ss, item, ' '); // index indx.html
if (!item.empty())
{
value_.push_back(item);
@ -17,7 +17,7 @@ void VectorDirective::parse(const std::string &value)
}
}
std::any VectorDirective::getValue() const
DirectiveValueType VectorDirective::getValue() const
{
return value_;
}

View File

@ -21,7 +21,7 @@ class VectorDirective : public ADirective
void parse(const std::string &value) override;
[[nodiscard]] std::any getValue() const override;
[[nodiscard]] DirectiveValueType getValue() const override;
private:
std::vector<std::string> value_;

View File

@ -86,8 +86,10 @@ void Server::setupServerSocket(const ServerConfig &config)
{
try
{
auto host = config["host"].get<std::string>();
auto port = config["listen"].get<int>();
std::unique_ptr<Socket> serverSocket = std::make_unique<Socket>();
serverSocket->bind(config["host"], config["listen"]);
serverSocket->bind(host, port);
serverSocket->listen(SOMAXCONN);
int server_fd = serverSocket->getFd();