1 //
2 // Copyright (C) 2023 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #pragma once
17 
18 #include <optional>
19 #include <string>
20 #include <tuple>
21 #include <type_traits>
22 #include <utility>
23 #include <vector>
24 
25 #include <android-base/logging.h>
26 #include <fruit/fruit.h>
27 
28 #include "common/libs/utils/result.h"
29 #include "common/libs/utils/type_name.h"
30 #include "host/libs/config/command_source.h"
31 #include "host/libs/config/feature.h"
32 #include "host/libs/config/kernel_log_pipe_provider.h"
33 
34 namespace cuttlefish {
35 
36 template <class...>
37 constexpr std::false_type CommandAlwaysFalse{};
38 
39 template <auto Fn, typename R, typename... Args>
40 class GenericCommandSource : public CommandSource,
41                              public KernelLogPipeConsumer {
42  public:
INJECT(GenericCommandSource (Args...args))43   INJECT(GenericCommandSource(Args... args))
44       : args_(std::forward_as_tuple(args...)) {}
45 
ResultSetup()46   Result<void> ResultSetup() override {
47     commands_.clear();
48     if constexpr (std::is_same_v<R, Result<std::vector<MonitorCommand>>>) {
49       commands_ = CF_EXPECT(std::apply(Fn, args_));
50     } else if constexpr (std::is_same_v<R, std::vector<MonitorCommand>>) {
51       commands_ = std::apply(Fn, args_);
52     } else if constexpr (std::is_same_v<R, Result<MonitorCommand>>) {
53       commands_.emplace_back(CF_EXPECT(std::apply(Fn, args_)));
54     } else if constexpr (std::is_same_v<R, MonitorCommand>) {
55       commands_.emplace_back(std::apply(Fn, args_));
56     } else if constexpr (std::is_same_v<
57                              R, Result<std::optional<MonitorCommand>>>) {
58       auto cmd = CF_EXPECT(std::apply(Fn, args_));
59       if (cmd) {
60         commands_.emplace_back(std::move(*cmd));
61       }
62     } else if constexpr (std::is_same_v<R, std::optional<MonitorCommand>>) {
63       auto cmd = std::apply(Fn, args_);
64       if (cmd) {
65         commands_.emplace_back(std::move(*cmd));
66       }
67     } else {
68       static_assert(CommandAlwaysFalse<R>, "Unexpected AutoCmd return type");
69     }
70     return {};
71   }
72 
Enabled()73   bool Enabled() const override {
74     return true;  // TODO(schuffelen): Delete `Enabled()`, it hasn't been useful
75   }
76 
Name()77   std::string Name() const override {
78     static constexpr auto kName = ValueName<Fn>();
79     return std::string(kName);
80   }
81 
Dependencies()82   std::unordered_set<SetupFeature*> Dependencies() const override {
83     return SetupFeatureDeps(args_);
84   }
85 
Commands()86   Result<std::vector<MonitorCommand>> Commands() override {
87     return std::move(commands_);
88   }
89 
90  private:
91   std::tuple<Args...> args_;
92   std::vector<MonitorCommand> commands_;
93 };
94 
95 template <auto Fn1, typename Fn2>
96 struct GenericCommandImpl;
97 
98 template <auto Fn, typename R, typename... Args>
99 struct GenericCommandImpl<Fn, R (*)(Args...)> {
100   using Type = GenericCommandSource<Fn, R, Args...>;
101 
102   static fruit::Component<
103       fruit::Required<typename std::remove_reference_t<Args>...>, Type>
104   Component() {
105     auto cmd = fruit::createComponent()
106                    .template addMultibinding<CommandSource, Type>()
107                    .template addMultibinding<SetupFeature, Type>();
108     constexpr bool uses_kernel_log_pipe =
109         (std::is_base_of_v<KernelLogPipeProvider,
110                            std::remove_reference_t<Args>> ||
111          ...);
112     if constexpr (uses_kernel_log_pipe) {
113       return cmd.template addMultibinding<KernelLogPipeConsumer, Type>();
114     } else {
115       return cmd;
116     }
117   }
118 };
119 
120 template <auto Fn>
121 using AutoCmd = GenericCommandImpl<Fn, decltype(Fn)>;
122 
123 }  // namespace cuttlefish
124