diff --git a/include/sqlgen.hpp b/include/sqlgen.hpp index 7bfd159..ff629e6 100644 --- a/include/sqlgen.hpp +++ b/include/sqlgen.hpp @@ -9,6 +9,7 @@ #include "sqlgen/Range.hpp" #include "sqlgen/Ref.hpp" #include "sqlgen/Result.hpp" +#include "sqlgen/Varchar.hpp" #include "sqlgen/read.hpp" #include "sqlgen/write.hpp" diff --git a/include/sqlgen/Varchar.hpp b/include/sqlgen/Varchar.hpp new file mode 100644 index 0000000..94b909e --- /dev/null +++ b/include/sqlgen/Varchar.hpp @@ -0,0 +1,101 @@ +#ifndef SQLGEN_VARCHAR_HPP_ +#define SQLGEN_VARCHAR_HPP_ + +#include +#include + +#include "Result.hpp" + +namespace sqlgen { + +template +class Varchar { + public: + using ReflectionType = std::string; + + static constexpr size_t size_ = _size; + + Varchar() : value_("") {} + + Varchar(const std::string& _value) : value_(check_size(_value)) {} + + Varchar(const char* _value) : value_(check_size(_value)) {} + + Varchar(Varchar<_size>&& _other) noexcept = default; + + Varchar(const Varchar<_size>& _other) = default; + + template + Varchar(const Varchar<_other_size>& _other) + : value_(check_size(_other.get())) {} + + template + Varchar(Varchar<_other_size>&& _other) : value_(check_size(_other.get())) {} + + ~Varchar() = default; + + static Result> make(const std::string& _value) noexcept { + try { + return Varchar(_value); + } catch (std::exception& e) { + return error(e.what()); + } + } + + /// Returns the underlying object. + const ReflectionType& get() const { return value_; } + + /// Returns the underlying object. + const ReflectionType& operator()() const { return value_; } + + /// Assigns the underlying object. + auto& operator=(const char* _value) { + value_ = check_size(_value); + return *this; + } + + /// Assigns the underlying object. + auto& operator=(const std::string& _value) { + value_ = check_size(_value); + return *this; + } + + /// Assigns the underlying object. + Varchar<_size>& operator=(const Varchar<_size>& _other) = default; + + /// Assigns the underlying object. + Varchar<_size>& operator=(Varchar<_size>&& _other) = default; + + /// Assigns the underlying object. + template + auto& operator=(const Varchar<_other_size>& _other) { + value_ = check_size(_other.get()); + return *this; + } + + /// Necessary for the automated transpilation to work. + const std::string& reflection() const { return value_; } + + static constexpr size_t size() { return size_; } + + /// Returns the underlying object. + const std::string& value() const { return value_; } + + private: + static std::string check_size(const std::string& _str) { + if (_str.size() > size_) { + throw std::runtime_error( + "String '" + _str + "' too long: " + std::to_string(_str.size()) + + " exceeds the maximum length of " + std::to_string(size_) + "."); + } + return _str; + } + + private: + /// The underlying value. + std::string value_; +}; + +} // namespace sqlgen + +#endif diff --git a/include/sqlgen/parsing/Parser.hpp b/include/sqlgen/parsing/Parser.hpp index ba9f2c0..3facdca 100644 --- a/include/sqlgen/parsing/Parser.hpp +++ b/include/sqlgen/parsing/Parser.hpp @@ -8,5 +8,6 @@ #include "Parser_shared_ptr.hpp" #include "Parser_string.hpp" #include "Parser_unique_ptr.hpp" +#include "Parser_varchar.hpp" #endif diff --git a/include/sqlgen/parsing/Parser_varchar.hpp b/include/sqlgen/parsing/Parser_varchar.hpp new file mode 100644 index 0000000..000afb1 --- /dev/null +++ b/include/sqlgen/parsing/Parser_varchar.hpp @@ -0,0 +1,36 @@ +#ifndef SQLGEN_PARSING_PARSER_VARCHAR_HPP_ +#define SQLGEN_PARSING_PARSER_VARCHAR_HPP_ + +#include +#include + +#include "../Result.hpp" +#include "../Varchar.hpp" +#include "../dynamic/Type.hpp" +#include "../dynamic/types.hpp" +#include "Parser_base.hpp" + +namespace sqlgen::parsing { + +template +struct Parser> { + static Result> read( + const std::optional& _str) noexcept { + return Parser::read(_str).and_then( + [](auto&& _t) -> Result> { + return Varchar<_size>::make(std::move(_t)); + }); + } + + static std::optional write(const Varchar<_size>& _v) noexcept { + return Parser::write(_v.value()); + } + + static dynamic::Type to_type() noexcept { + return dynamic::types::VarChar{.length = _size}; + } +}; + +} // namespace sqlgen::parsing + +#endif diff --git a/tests/sqlite/test_varchar.cpp b/tests/sqlite/test_varchar.cpp new file mode 100644 index 0000000..41bf023 --- /dev/null +++ b/tests/sqlite/test_varchar.cpp @@ -0,0 +1,39 @@ +#include + +#include +#include +#include +#include +#include + +namespace test_varchar { + +struct Person { + sqlgen::PrimaryKey id; + sqlgen::Varchar<6> first_name; + sqlgen::Varchar<7> last_name; + int age; +}; + +TEST(sqlite, test_varchar) { + const auto people1 = std::vector( + {Person{ + .id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45}, + Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10}, + Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8}, + Person{ + .id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0}}); + + const auto conn = sqlgen::sqlite::connect(); + + sqlgen::write(conn, people1); + + const auto people2 = sqlgen::read>(conn).value(); + + const auto json1 = rfl::json::write(people1); + const auto json2 = rfl::json::write(people2); + + EXPECT_EQ(json1, json2); +} + +} // namespace test_varchar