1#pragma once
2
3#include "units/template.hpp"
4
5#include <chrono>
6#include <functional>
7#include <iostream>
8#include <optional>
9#include <string>
10#include <toml++/toml.hpp>
11#include <vector>
12
13struct UnitStatus
14{
15 enum MajorStatus
16 {
17 UnitStopped,
18 UnitStarting,
19 UnitStarted,
20 UnitFailed,
21 UnitStopping,
22 };
23
24 bool active = false;
25 MajorStatus status = UnitStarting;
26 std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now();
27 std::string message;
28
29 void Inactive()
30 {
31 active = false, status = UnitStopped, message.clear();
32 UpdateTimestamp();
33 }
34
35 void Starting(const std::string &msg = "starting...")
36 {
37 active = true, status = UnitStarting, message = msg;
38 UpdateTimestamp();
39 }
40
41 void Started(const std::string &msg = "success")
42 {
43 active = true, status = UnitStarted, message = msg;
44 UpdateTimestamp();
45 }
46
47 void Failed(const std::string &msg = "failed")
48 {
49 active = true, status = UnitFailed, message = msg;
50 UpdateTimestamp();
51 }
52
53 void Stopping(const std::string &msg = "stopping...")
54 {
55 active = true, status = UnitStopping, message = msg;
56 UpdateTimestamp();
57 }
58
59 private:
60 void UpdateTimestamp()
61 {
62 timestamp = std::chrono::system_clock::now();
63 }
64};
65
66enum class UnitType
67{
68 Inherited = -1,
69
70 Service, // 0
71 Target, // 1
72 Path, // 2
73 Mount, // 3
74 Symlink, // 4
75 Device, // 5
76 Timer, // 6
77};
78
79// clang-format off
80struct toplevel_t
81{
82 enum class _Construct { _Token };
83 explicit consteval toplevel_t(_Construct) noexcept {};
84};
85struct custom_arg_t
86{
87 enum class _Construct { _Token };
88 explicit consteval custom_arg_t(_Construct) noexcept {};
89};
90// clang-format on
91
92struct IUnit : public std::enable_shared_from_this<IUnit>
93{
94 const std::string id;
95
96 explicit IUnit(const std::string &id) : id(id) {};
97 virtual ~IUnit() = default;
98
99 std::string GetBaseId() const
100 {
101 std::string ret = id;
102 if (const auto it = ret.find(c: '@'); it != std::string::npos)
103 ret = id.substr(pos: 0, n: it);
104
105 if (ret.ends_with(x: TEMPLATE_SUFFIX))
106 return ret.substr(pos: 0, n: ret.size() - TEMPLATE_SUFFIX.size());
107
108 return ret;
109 }
110
111 virtual UnitType GetType() const = 0;
112 virtual bool Start() = 0;
113 virtual bool Stop() = 0;
114 virtual std::string GetDescription() const = 0;
115 virtual const UnitStatus &GetStatus() const = 0;
116 virtual std::vector<std::string> GetDependencies() const = 0;
117 virtual std::vector<std::string> GetPartOf() const = 0;
118 virtual std::optional<std::string> GetFailReason() const = 0;
119 virtual void AddDependency(const std::string &depName) = 0;
120};
121
122using UnitCreatorType = std::function<std::shared_ptr<IUnit>(const std::string &, toml::table &)>;
123using UnitInstantiator = std::function<std::shared_ptr<IUnit>(const std::string &, std::shared_ptr<const Template> template_, const ArgumentMap &args)>;
124
125struct Unit : public IUnit
126{
127 friend std::ostream &operator<<(std::ostream &, const Unit &);
128
129 private:
130 static constexpr auto inline toplevel = toplevel_t{ toplevel_t::_Construct::_Token };
131 static constexpr auto inline custom_arg = custom_arg_t{ custom_arg_t::_Construct::_Token };
132
133 public:
134 static const std::map<std::string, UnitCreatorType> &Creator(std::optional<std::pair<std::string, UnitCreatorType>> creator = std::nullopt);
135 static const std::map<std::string, UnitInstantiator> &Instantiator(std::optional<std::pair<std::string, UnitInstantiator>> instantiator = std::nullopt);
136
137 static std::shared_ptr<IUnit> Create(const std::string &id, const toml::table &data);
138 static std::shared_ptr<IUnit> Instantiate(const std::string &id, std::shared_ptr<const Template> template_, const ArgumentMap &args);
139
140 static void VerifyUnitArguments(const std::string &id, const toml::table &table);
141
142 static std::string replace_all(std::string str, const std::string_view matcher, const std::string_view replacement)
143 {
144 size_t pos = 0;
145 while ((pos = str.find(svt: matcher, pos: pos)) != std::string::npos)
146 {
147 str.replace(pos: pos, n: matcher.length(), svt: replacement);
148 pos += replacement.length();
149 }
150 return str;
151 }
152
153 public:
154 const ArgumentMap arguments;
155 const std::string description;
156
157 public:
158 explicit Unit(const std::string &id, toml::table &table, std::shared_ptr<const Template> template_ = nullptr, const ArgumentMap &args = {});
159 virtual ~Unit() override = default;
160
161 public:
162 virtual UnitType GetType() const override = 0;
163 virtual bool Start() override = 0;
164 virtual bool Stop() override = 0;
165
166 public:
167 // clang-format off
168 std::string GetDescription() const override { return description; }
169 std::vector<std::string> GetDependencies() const override { return dependsOn; }
170 std::vector<std::string> GetPartOf() const override { return partOf; }
171 void AddDependency(const std::string &id) override { dependsOn.push_back(x: id); }
172 const UnitStatus &GetStatus() const override { return status; }
173 // clang-format on
174
175 std::optional<std::string> GetFailReason() const override
176 {
177 if (status.status == UnitStatus::UnitFailed)
178 return status.message;
179 return std::nullopt;
180 }
181
182 protected:
183 std::string PopArg(toml::table &table, std::string_view key);
184 std::vector<std::string> GetArrayArg(toml::table &table, std::string_view key);
185
186 private:
187 std::string PopArg(toml::table &table, std::string_view key, toplevel_t);
188 std::vector<std::string> GetArrayArg(toml::table &table, std::string_view key, toplevel_t);
189
190 private:
191 virtual void onPrint(std::ostream &) const {};
192 std::string ReplaceArgs(const std::string &str) const;
193 std::vector<std::string> ReplaceArgs(const toml::array *array);
194
195 protected:
196 UnitStatus status;
197
198 private:
199 std::vector<std::string> dependsOn;
200 std::vector<std::string> partOf;
201 const std::shared_ptr<const Template> template_;
202};
203
204template<typename TUnit>
205struct UnitRegisterer
206{
207 static constexpr auto Creator(const std::string &id, toml::table &table)
208 {
209 return std::static_pointer_cast<IUnit>(std::make_shared<TUnit>(id, table));
210 }
211
212 static constexpr auto Instantiator(const std::string &id, std::shared_ptr<const Template> template_, const ArgumentMap &args)
213 {
214 auto table = template_->table;
215 const auto ptr = std::static_pointer_cast<IUnit>(std::make_shared<TUnit>(id, table, template_, args));
216 table.erase(key: "template_params");
217 Unit::VerifyUnitArguments(id, table);
218 return ptr;
219 }
220
221 explicit UnitRegisterer(const std::string &type)
222 {
223 Unit::Creator(creator: std::make_pair(type, Creator));
224 Unit::Instantiator(instantiator: std::make_pair(type, Instantiator));
225 }
226};
227
228#define RegisterUnit(name, Name) static __attribute__((__used__)) UnitRegisterer<Name> __register_##name(#name)
229