blob: 74e0907a35b85c12cf1c3c68526958691269c146 [file] [log] [blame]
Patrick Williams10010b12022-08-28 14:56:07 -05001#pragma once
2
3#include <sdbusplus/async/callback.hpp>
4#include <sdbusplus/async/context.hpp>
5#include <sdbusplus/message.hpp>
6
7#include <string>
8#include <string_view>
9#include <type_traits>
10#include <unordered_map>
11#include <variant>
12
13namespace sdbusplus::async
14{
15namespace proxy_ns
16{
17/** A (client-side) proxy to a dbus object.
18 *
19 * A dbus object is referenced by 3 address pieces:
20 * - The service hosting the object.
21 * - The path the object resides at.
22 * - The interface the object implements.
23 * The proxy is a holder of these 3 addresses.
24 *
25 * One all 3 pieces of the address are known by the proxy, the proxy
26 * can be used to perform normal dbus operations: method-call, get-property
27 * set-property, get-all-properties.
28 *
29 * Addresses are supplied to the object by calling the appropriate method:
30 * service, path, interface. These methods return a _new_ object with the
31 * new information filled in.
32 *
33 * If all pieces are known at compile-time it is suggested to be constructed
34 * similar to the following:
35 *
36 * ```
37 * constexpr auto systemd = sdbusplus::async::proxy()
38 * .service("org.freedesktop.systemd1")
39 * .path("/org/freedesktop/systemd1")
40 * .interface("org.freedesktop.systemd1.Manager");
41 * ```
42 *
43 * The proxy object can be filled as information is available and attempts
44 * to be as effecient as possible (supporting constexpr construction and
45 * using std::string_view mostly). In some cases it is necessary for the
46 * proxy to leave a scope where it would be no longer safe to use the
47 * previously-supplied string_views. The `preserve` operation can be used
48 * to transform an existing proxy into one which is safe to leave (because
49 * it uses less-efficient but safe std::string values).
50 */
51template <bool S = false, bool P = false, bool I = false,
52 bool Preserved = false>
53struct proxy : private sdbusplus::bus::details::bus_friend
54{
55 // Some typedefs to reduce template noise...
56
57 using string_t =
58 std::conditional_t<Preserved, std::string, std::string_view>;
59 using string_ref = const string_t&;
60 using sv_ref = const std::string_view&;
61
62 template <bool V>
63 using value_t = std::conditional_t<V, string_t, std::monostate>;
64 template <bool V>
65 using value_ref = const value_t<V>&;
66
67 // Default constructor should only work for the "all empty" case.
68 constexpr proxy()
69 requires(!S && !P && !I)
70 = default;
71 constexpr proxy()
72 requires(S || P || I)
73 = delete;
74
75 // Construtor allowing all 3 to be passed in.
76 constexpr proxy(value_ref<S> s, value_ref<P> p, value_ref<I> i) :
77 s(s), p(p), i(i){};
78
79 // Functions to assign address fields.
80 constexpr auto service(string_ref s) const noexcept
81 requires(!S)
82 {
83 return proxy<true, P, I, Preserved>{s, this->p, this->i};
84 }
85 constexpr auto path(string_ref p) const noexcept
86 requires(!P)
87 {
88 return proxy<S, true, I, Preserved>{this->s, p, this->i};
89 }
90 constexpr auto interface(string_ref i) const noexcept
91 requires(!I)
92 {
93 return proxy<S, P, true, Preserved>{this->s, this->p, i};
94 }
95
96 /** Make a copyable / returnable proxy.
97 *
98 * Since proxy deals with string_view by default, for efficiency,
99 * there are cases where it would be dangerous for a proxy object to
100 * leave a scope either by a return or a pass into a lambda. This
101 * function will convert an existing proxy into one backed by
102 * `std::string` so that it can safely leave a scope.
103 */
104 auto preserve() const noexcept
105 requires(!Preserved)
106 {
Patrick Williamsf1d999b2024-01-24 08:05:21 -0600107 using result_t = proxy<S, P, I, true>;
Ed Tanouse12a23c2024-02-22 15:28:58 -0800108 return result_t(typename result_t::template value_t<S>(this->s),
109 typename result_t::template value_t<P>(this->p),
110 typename result_t::template value_t<I>(this->i));
Patrick Williams10010b12022-08-28 14:56:07 -0500111 }
112
113 /** Perform a method call.
114 *
115 * @tparam Rs - The return type(s) of the method call.
116 * @tparam Ss - The parameter type(s) of the method call.
117 *
118 * @param[in] ctx - The context to use.
119 * @param[in] method - The method name.
120 * @param[in] ss - The calling parameters.
121 *
122 * @return A Sender which completes with either { void, Rs, tuple<Rs...> }.
123 */
124 template <typename... Rs, typename... Ss>
125 auto call(context& ctx, sv_ref method, Ss&&... ss) const
126 requires((S) && (P) && (I))
127 {
128 // Create the method_call message.
129 auto msg = ctx.get_bus().new_method_call(c_str(s), c_str(p), c_str(i),
130 method.data());
131 if constexpr (sizeof...(Ss) > 0)
132 {
133 msg.append(std::forward<Ss>(ss)...);
134 }
135
136 // Use 'callback' to perform the operation and "then" "unpack" the
137 // contents.
Patrick Williams6db88382023-10-20 11:18:17 -0500138 return callback([bus = get_busp(ctx),
139 msg = std::move(msg)](auto cb, auto data) mutable {
Patrick Williamsd2149042023-05-10 07:50:13 -0500140 return sd_bus_call_async(bus, nullptr, msg.get(), cb, data, 0);
141 }) | execution::then([](message_t&& m) { return m.unpack<Rs...>(); });
Patrick Williams10010b12022-08-28 14:56:07 -0500142 }
143
144 /** Get a property.
145 *
146 * @tparam T - The type of the property.
147 *
148 * @param[in] ctx - The context to use.
149 * @param[in] property - The property name.
150 *
151 * @return A Sender which completes with T as the property value.
152 */
153 template <typename T>
154 auto get_property(context& ctx, sv_ref property) const
155 requires((S) && (P) && (I))
156 {
157 using result_t = std::variant<T>;
158 auto prop_intf = proxy(s, p, dbus_prop_intf);
159
160 return prop_intf.template call<result_t>(ctx, "Get", c_str(i),
161 property.data()) |
162 execution::then([](result_t&& v) { return std::get<T>(v); });
163 }
164
165 /** Get all properties.
166 *
167 * @tparam V - The variant type of all possible properties.
168 *
169 * @param[in] ctx - The context to use.
170 *
171 * @return A Sender which completes with unordered_map<string, V>.
172 */
173 template <typename V>
174 auto get_all_properties(context& ctx) const
175 requires((S) && (P) && (I))
176 {
177 using result_t = std::unordered_map<std::string, V>;
178 auto prop_intf = proxy(s, p, dbus_prop_intf);
179
180 return prop_intf.template call<result_t>(ctx, "GetAll", c_str(i));
181 }
182
183 /** Set a property.
184 *
185 * @tparam T - The type of the property (usually deduced by the compiler).
186 *
187 * @param[in] ctx - The context to use.
188 * @param[in] property - The property name.
189 * @param[in] value - The value to set.
190 *
191 * @return A Sender which completes void when the property is set.
192 */
193 template <typename T>
194 auto set_property(context& ctx, sv_ref property, T&& value) const
195 requires((S) && (P) && (I))
196 {
197 auto prop_intf = proxy(s, p, dbus_prop_intf);
Patrick Williams1f3efd82023-04-25 15:52:56 -0500198 return prop_intf.template call<>(
199 ctx, "Set", c_str(i), property.data(),
200 std::variant<std::decay_t<T>>{std::forward<T>(value)});
Patrick Williams10010b12022-08-28 14:56:07 -0500201 }
202
203 private:
Patrick Williamsb274c312023-04-25 15:30:01 -0500204 static constexpr auto dbus_prop_intf = "org.freedesktop.DBus.Properties";
Patrick Williams10010b12022-08-28 14:56:07 -0500205
206 // Helper to get the underlying c-string of a string_view or string.
207 static auto c_str(string_ref v)
208 {
209 if constexpr (Preserved)
210 {
211 return v.c_str();
212 }
213 else
214 {
215 return v.data();
216 }
217 }
218
219 value_t<S> s = {};
220 value_t<P> p = {};
221 value_t<I> i = {};
222};
223
224} // namespace proxy_ns
225
226// clang currently has problems with the intersect of default template
227// parameters and concepts. I've opened llvm/llvm-project#57646 and added
228// this indirect.
229using proxy = proxy_ns::proxy<>;
230
231// Sometimes it is useful to hold onto a proxy, such as in a class member, so
232// define a type alias for one which can be safely held.
233using finalized_proxy = proxy_ns::proxy<true, true, true, true>;
234
235} // namespace sdbusplus::async