summaryrefslogtreecommitdiff
path: root/libarttools/include/tools/cmdline_builder.h
blob: 4f3727b133aebfbb9def774869ba17f782477bd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ART_LIBARTTOOLS_INCLUDE_TOOLS_CMDLINE_BUILDER_H_
#define ART_LIBARTTOOLS_INCLUDE_TOOLS_CMDLINE_BUILDER_H_

#include <algorithm>
#include <iterator>
#include <string>
#include <string_view>
#include <vector>

#include "android-base/stringprintf.h"

namespace art {
namespace tools {

namespace internal {

constexpr bool ContainsOneFormatSpecifier(std::string_view format, char specifier) {
  int count = 0;
  size_t pos = 0;
  while ((pos = format.find('%', pos)) != std::string_view::npos) {
    if (pos == format.length() - 1) {
      // Invalid trailing '%'.
      return false;
    }
    if (format[pos + 1] == specifier) {
      count++;
    } else if (format[pos + 1] != '%') {
      // "%%" is okay. Otherwise, it's a wrong specifier.
      return false;
    }
    pos += 2;
  }
  return count == 1;
}

}  // namespace internal

// A util class that builds cmdline arguments.
class CmdlineBuilder {
 public:
  // Returns all arguments.
  const std::vector<std::string>& Get() const { return elements_; }

  // Adds an argument as-is.
  CmdlineBuilder& Add(std::string_view arg) {
    elements_.push_back(std::string(arg));
    return *this;
  }

  // Same as above but adds a runtime argument.
  CmdlineBuilder& AddRuntime(std::string_view arg) { return Add("--runtime-arg").Add(arg); }

  // Adds a string value formatted by the format string.
  //
  // Usage: Add("--flag=%s", "value")
  CmdlineBuilder& Add(const char* arg_format, const std::string& value)
      __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'),
                               "'arg' must be a string literal that contains '%s'"))) {
    return Add(android::base::StringPrintf(arg_format, value.c_str()));
  }

  // Same as above but adds a runtime argument.
  CmdlineBuilder& AddRuntime(const char* arg_format, const std::string& value)
      __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'),
                               "'arg' must be a string literal that contains '%s'"))) {
    return AddRuntime(android::base::StringPrintf(arg_format, value.c_str()));
  }

  // Adds an integer value formatted by the format string.
  //
  // Usage: Add("--flag=%d", 123)
  CmdlineBuilder& Add(const char* arg_format, int value)
      __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 'd'),
                               "'arg' must be a string literal that contains '%d'"))) {
    return Add(android::base::StringPrintf(arg_format, value));
  }

  // Same as above but adds a runtime argument.
  CmdlineBuilder& AddRuntime(const char* arg_format, int value)
      __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 'd'),
                               "'arg' must be a string literal that contains '%d'"))) {
    return AddRuntime(android::base::StringPrintf(arg_format, value));
  }

  // Adds a string value formatted by the format string if the value is non-empty. Does nothing
  // otherwise.
  //
  // Usage: AddIfNonEmpty("--flag=%s", "value")
  CmdlineBuilder& AddIfNonEmpty(const char* arg_format, const std::string& value)
      __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'),
                               "'arg' must be a string literal that contains '%s'"))) {
    if (!value.empty()) {
      Add(android::base::StringPrintf(arg_format, value.c_str()));
    }
    return *this;
  }

  // Same as above but adds a runtime argument.
  CmdlineBuilder& AddRuntimeIfNonEmpty(const char* arg_format, const std::string& value)
      __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'),
                               "'arg' must be a string literal that contains '%s'"))) {
    if (!value.empty()) {
      AddRuntime(android::base::StringPrintf(arg_format, value.c_str()));
    }
    return *this;
  }

  // Adds an argument as-is if the boolean value is true. Does nothing otherwise.
  CmdlineBuilder& AddIf(bool value, std::string_view arg) {
    if (value) {
      Add(arg);
    }
    return *this;
  }

  // Same as above but adds a runtime argument.
  CmdlineBuilder& AddRuntimeIf(bool value, std::string_view arg) {
    if (value) {
      AddRuntime(arg);
    }
    return *this;
  }

  // Concatenates this builder with another. Returns the concatenated result and nullifies the input
  // builder.
  CmdlineBuilder& Concat(CmdlineBuilder&& other) {
    elements_.reserve(elements_.size() + other.elements_.size());
    std::move(other.elements_.begin(), other.elements_.end(), std::back_inserter(elements_));
    other.elements_.clear();
    return *this;
  }

 private:
  std::vector<std::string> elements_;
};

}  // namespace tools
}  // namespace art

#endif  // ART_LIBARTTOOLS_INCLUDE_TOOLS_CMDLINE_BUILDER_H_