FlatImage
A configurable Linux containerization system
Loading...
Searching...
No Matches
db.hpp
Go to the documentation of this file.
1
8
9#pragma once
10
11#include <filesystem>
12#include <fstream>
13#include <nlohmann/json.hpp>
14#include <variant>
15
16#include "../std/expected.hpp"
17#include "../std/concept.hpp"
18#include "../macro.hpp"
19
29namespace ns_db
30{
31
32using object_t = nlohmann::basic_json<>::object_t;
33class Db;
34
35namespace
36{
37
38namespace fs = std::filesystem;
39
40
41using json_t = nlohmann::json;
42
43template<typename T>
44concept IsString =
45 std::convertible_to<std::decay_t<T>, std::string>
46 or std::constructible_from<std::string, std::decay_t<T>>;
47
48} // anonymous namespace
49
50using KeyType = json_t::value_t;
51
63class Db
64{
65 private:
73 std::variant<json_t, std::reference_wrapper<json_t>> m_json;
74 public:
75 // Constructors
76 Db() noexcept;
77 template<typename T> requires std::same_as<std::remove_cvref_t<T>, json_t>
78 explicit Db(T&& json) noexcept;
79 template<typename T> requires std::same_as<std::remove_cvref_t<T>, json_t>
80 explicit Db(std::reference_wrapper<T> const& json) noexcept;
81 // Element access
82 [[maybe_unused]] [[nodiscard]] std::vector<std::string> keys() const noexcept;
83 [[maybe_unused]] [[nodiscard]] std::vector<std::pair<std::string, Db>> items() const noexcept;
84 template<typename V = Db>
85 [[maybe_unused]] [[nodiscard]] Value<V> value() noexcept;
89 template<typename F, IsString Ks>
90 [[maybe_unused]] [[nodiscard]] decltype(auto) apply(F&& f, Ks&& ks);
91 [[maybe_unused]] [[nodiscard]] Value<std::string> dump();
92 // Capacity
93 [[maybe_unused]] [[nodiscard]] bool empty() const noexcept;
94 // Lookup
95 template<IsString T>
96 [[maybe_unused]] [[nodiscard]] bool contains(T&& t) const noexcept;
97 [[maybe_unused]] [[nodiscard]] KeyType type() const noexcept;
98 // Modifiers
99 template<IsString T>
100 [[maybe_unused]] [[nodiscard]] bool erase(T&& t);
101 [[maybe_unused]] void clear();
102 json_t& data();
103 json_t const& data() const;
104 // Operators
105 template<typename T>
106 Db& operator=(T&& t);
107 [[maybe_unused]] [[nodiscard]] Db operator()(std::string const& t);
108 // Friends
109 friend std::ostream& operator<<(std::ostream& os, Db const& db);
110};
111
118inline Db::Db() noexcept : m_json(json_t::object())
119{
120}
121
133template<typename T> requires std::same_as<std::remove_cvref_t<T>, json_t>
134inline Db::Db(std::reference_wrapper<T> const& json) noexcept : m_json(json)
135{
136}
137
148template<typename T> requires std::same_as<std::remove_cvref_t<T>, json_t>
149inline Db::Db(T&& json) noexcept : m_json(json)
150{
151}
152
163inline json_t& Db::data()
164{
165 if (std::holds_alternative<std::reference_wrapper<json_t>>(m_json))
166 {
167 return std::get<std::reference_wrapper<json_t>>(m_json).get();
168 }
169
170 return std::get<json_t>(m_json);
171}
172
182inline json_t const& Db::data() const
183{
184 return const_cast<Db*>(this)->data();
185}
186
197inline std::vector<std::string> Db::keys() const noexcept
198{
199 json_t const& json = data();
200 return_if(not json.is_structured(), {}, "E::Invalid non-structured json access");
201 return json.items()
202 | std::views::transform([&](auto&& e) { return e.key(); })
203 | std::ranges::to<std::vector<std::string>>();
204}
205
216inline std::vector<std::pair<std::string,Db>> Db::items() const noexcept
217{
218 json_t const& json = data();
219 return_if(not json.is_structured(), {}, "E::Invalid non-structured json access");
220 return json.items()
221 | std::views::transform([](auto&& e){ return std::make_pair(e.key(), Db{e.value()}); })
222 | std::ranges::to<std::vector<std::pair<std::string,Db>>>();
223}
224
237template<typename V>
239{
240 json_t& json = data();
241 if constexpr ( std::same_as<V,Db> )
242 {
243 return Db{json};
244 }
246 {
247 return_if(not json.is_array(), Error("D::Tried to create array with non-array entry"));
248 return_if(std::any_of(json.begin(), json.end(), [](auto&& e){ return not e.is_string(); })
249 , Error("D::Invalid key type for string array")
250 );
251 return std::ranges::subrange(json.begin(), json.end())
252 | std::views::transform([](auto&& e){ return typename std::remove_cvref_t<V>::value_type(e); })
253 | std::ranges::to<V>();
254 }
255 else if constexpr (ns_concept::StringConstructible<V>)
256 {
257 return ( json.is_string() )?
258 Value<V>(std::string{json})
259 : Error("D::Json element is not a string");
260 }
261 else
262 {
263 static_assert(std::is_same_v<V, V> == false, "Unsupported type V for value()");
264 return Error("D::No viable type conversion");
265 }
266}
267
278{
279 return Try(data().dump(2));
280}
281
291inline bool Db::empty() const noexcept
292{
293 return data().empty();
294}
295
308template<IsString T>
309bool Db::contains(T&& key) const noexcept
310{
311 json_t const& json = data();
312 return (json.is_object() || json.is_array()) && json.contains(key);
313}
314
324inline KeyType Db::type() const noexcept
325{
326 return data().type();
327}
328
342template<IsString T>
343bool Db::erase(T&& key)
344{
345 auto str_key = ns_string::to_string(key);
346
347 json_t& json = data();
348
349 if ( json.is_array() )
350 {
351 auto it_search = std::find(json.begin(), json.end(), str_key);
352 if ( it_search == json.end() ) { return false; }
353 json.erase(std::distance(json.begin(), it_search));
354 return true;
355 }
356 else if(json.is_object())
357 {
358 return json.erase(str_key) > 0;
359 }
360
361 return false;
362}
363
370inline void Db::clear()
371{
372 data() = json_t::object();
373}
374
386template<typename T>
388{
389 data() = value;
390 return *this;
391}
392
404inline Db Db::operator()(std::string const& key)
405{
406 json_t& json = data();
407
408 if(json.is_object() or json.empty())
409 {
410 return Db{std::reference_wrapper<json_t>(json[key])};
411 }
412
413 return *this;
414}
415
427inline std::ostream& operator<<(std::ostream& os, Db const& db)
428{
429 os << db.data();
430 return os;
431}
432
444[[nodiscard]] inline Value<Db> read_file(fs::path const& path_file_db)
445{
446 return_if(not Try(fs::exists(path_file_db)), Error("D::Invalid db file '{}'", path_file_db.string()));
447 // Parse a file
448 auto f_parse_file = [](std::ifstream const& f) -> Value<json_t>
449 {
450 // Read to string
451 std::string contents = ns_string::to_string(f.rdbuf());
452 // Validate contents and return parsed result
453 return Try(json_t::parse(contents));
454 };
455 // Open target file as read
456 std::ifstream file(path_file_db, std::ios::in);
457 return_if(not file.is_open(), Error("D::Failed to open '{}'", path_file_db.string()));
458 // Try to parse
459 return Db(Pop(f_parse_file(file)));
460}
461
474[[nodiscard]] inline Value<void> write_file(fs::path const& path_file_db, Db& db)
475{
476 std::ofstream file(path_file_db, std::ios::trunc);
477 return_if(not file.is_open(), Error("D::Failed to open '{}' for writing", path_file_db.string()));
478 file << std::setw(2) << Pop(db.dump());
479 file.close();
480 return Value<void>{};
481}
482
495template<ns_concept::StringRepresentable S>
497{
498 std::string data = ns_string::to_string(s);
499 return_if(data.empty(), Error("D::Empty json data"));
500 return Try(Db{json_t::parse(data)}, "D::Could not parse json file");
501}
502
503} // namespace ns_db
504
505/* vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et :*/
A type-safe wrapper around nlohmann::json for database operations.
Definition db.hpp:64
bool contains(T &&t) const noexcept
Checks if the JSON contains a specific key.
Definition db.hpp:309
Db() noexcept
Constructs a new Db object with an empty JSON object.
Definition db.hpp:118
friend std::ostream & operator<<(std::ostream &os, Db const &db)
Outputs the JSON data to an output stream.
Definition db.hpp:427
std::vector< std::string > keys() const noexcept
Retrieves all keys from the current JSON object or array.
Definition db.hpp:197
KeyType type() const noexcept
Retrieves the type of the current JSON element.
Definition db.hpp:324
Db operator()(std::string const &t)
Accesses or creates a nested JSON entry by key.
Definition db.hpp:404
std::vector< std::pair< std::string, Db > > items() const noexcept
Retrieves key-value pairs as Db objects from the current JSON.
Definition db.hpp:216
void clear()
Resets the JSON data to an empty object.
Definition db.hpp:370
json_t & data()
Retrieves a mutable reference to the underlying JSON data.
Definition db.hpp:163
bool erase(T &&t)
Removes a key from the JSON structure.
Definition db.hpp:343
decltype(auto) apply(F &&f, Ks &&ks)
Value< V > value() noexcept
Converts the current JSON entry to a specified type.
Definition db.hpp:238
Value< std::string > dump()
Serializes the current JSON data to a formatted string.
Definition db.hpp:277
Db & operator=(T &&t)
Assigns a new value to the underlying JSON data.
Definition db.hpp:387
bool empty() const noexcept
Checks whether the JSON data is empty.
Definition db.hpp:291
Custom C++ concepts for type constraints and compile-time validation.
Concept for std::vector types.
Definition concept.hpp:276
Concept for types that can construct a std::string.
Definition concept.hpp:365
Concept to check if all types are the same.
Definition concept.hpp:198
Enhanced error handling framework built on std::expected.
Simplified macros for common control flow patterns with optional logging.
JSON-based configuration database layer.
Value< Db > from_string(S &&s)
Parses a JSON string and creates a Db object.
Definition db.hpp:496
Value< void > write_file(fs::path const &path_file_db, Db &db)
Serializes a Db object and writes it to a JSON file.
Definition db.hpp:474
Value< Db > read_file(fs::path const &path_file_db)
Deserializes a JSON file into a Db object.
Definition db.hpp:444
std::string to_string(T &&t) noexcept
Converts a type to a string.
Definition string.hpp:97
Enhanced expected type with integrated logging capabilities.
Definition expected.hpp:44