• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GothenburgBitFactory / taskwarrior / 10152339701

29 Jul 2024 09:45PM UTC coverage: 84.437% (+0.07%) from 84.372%
10152339701

push

github

web-flow
Merge pull request #3566 from felixschurk/add-clang-format

Add clang-format to enforce style guide

12359 of 13760 new or added lines in 147 files covered. (89.82%)

123 existing lines in 42 files now uncovered.

19070 of 22585 relevant lines covered (84.44%)

19724.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.49
/src/commands/CmdConfig.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included
13
// in all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
// SOFTWARE.
22
//
23
// https://www.opensource.org/licenses/mit-license.php
24
//
25
////////////////////////////////////////////////////////////////////////////////
26

27
#include <cmake.h>
28
// cmake.h include header must come first
29

30
#include <CmdConfig.h>
31
#include <Context.h>
32
#include <JSON.h>
33
#include <format.h>
34
#include <shared.h>
35

36
#include <algorithm>
37
#include <sstream>
38

39
////////////////////////////////////////////////////////////////////////////////
40
CmdConfig::CmdConfig() {
4,389✔
41
  _keyword = "config";
4,389✔
42
  _usage = "task          config [name [value | '']]";
4,389✔
43
  _description = "Change settings in the task configuration";
4,389✔
44
  _read_only = true;
4,389✔
45
  _displays_id = false;
4,389✔
46
  _needs_gc = false;
4,389✔
47
  _uses_context = false;
4,389✔
48
  _accepts_filter = false;
4,389✔
49
  _accepts_modifications = false;
4,389✔
50
  _accepts_miscellaneous = true;
4,389✔
51
  _category = Command::Category::config;
4,389✔
52
}
4,389✔
53

54
////////////////////////////////////////////////////////////////////////////////
55
bool CmdConfig::setConfigVariable(const std::string& name, const std::string& value,
893✔
56
                                  bool confirmation /* = false */) {
57
  // Read .taskrc (or equivalent)
58
  std::vector<std::string> contents;
893✔
59
  File::read(Context::getContext().config.file(), contents);
893✔
60

61
  auto found = false;
893✔
62
  auto change = false;
893✔
63

64
  for (auto& line : contents) {
5,733✔
65
    // Get l-trimmed version of the line
66
    auto trimmed_line = trim(line, " ");
9,680✔
67

68
    // If there is a comment on the line, it must follow the pattern.
69
    auto comment = line.find('#');
4,840✔
70
    auto pos = trimmed_line.find(name + '=');
4,840✔
71

72
    // TODO: Use std::regex here
73
    if (pos == 0) {
4,840✔
74
      found = true;
55✔
75
      if (!confirmation ||
102✔
76
          confirm(format("Are you sure you want to change the value of '{1}' from '{2}' to '{3}'?",
204✔
77
                         name, Context::getContext().config.get(name), value))) {
102✔
78
        auto new_line = line.substr(0, pos + name.length() + 1) + json::encode(value);
110✔
79

80
        // Preserve the comment
81
        if (comment != std::string::npos) new_line += "  " + line.substr(comment);
55✔
82

83
        // Rewrite the line
84
        line = new_line;
55✔
85
        change = true;
55✔
86
      }
55✔
87
    }
88
  }
4,840✔
89

90
  // Not found, so append instead.
91
  if (!found &&
1,731✔
92
      (!confirmation ||
838✔
93
       confirm(format("Are you sure you want to add '{1}' with a value of '{2}'?", name, value)))) {
1,665✔
94
    contents.push_back(name + '=' + json::encode(value));
812✔
95
    change = true;
812✔
96
  }
97

98
  if (change) File::write(Context::getContext().config.file(), contents);
893✔
99

100
  return change;
893✔
101
}
893✔
102

103
////////////////////////////////////////////////////////////////////////////////
104
int CmdConfig::unsetConfigVariable(const std::string& name, bool confirmation /* = false */) {
20✔
105
  // Read .taskrc (or equivalent)
106
  std::vector<std::string> contents;
20✔
107
  File::read(Context::getContext().config.file(), contents);
20✔
108

109
  auto found = false;
20✔
110
  auto change = false;
20✔
111

112
  for (auto line = contents.begin(); line != contents.end();) {
130✔
113
    auto lineDeleted = false;
110✔
114

115
    // Get l-trimmed version of the line
116

117
    // If there is a comment on the line, it must follow the pattern.
118
    auto pos = trim(*line, " ").find(name + '=');
110✔
119

120
    // TODO: Use std::regex here
121
    if (pos == 0) {
110✔
122
      found = true;
11✔
123

124
      // Remove name
125
      if (!confirmation || confirm(format("Are you sure you want to remove '{1}'?", name))) {
11✔
126
        // vector::erase method returns a valid iterator to the next object
127
        line = contents.erase(line);
11✔
128
        lineDeleted = true;
11✔
129
        change = true;
11✔
130
      }
131
    }
132

133
    if (!lineDeleted) line++;
110✔
134
  }
135

136
  if (change) File::write(Context::getContext().config.file(), contents);
20✔
137

138
  if (change && found)
20✔
139
    return 0;
11✔
140
  else if (found)
9✔
141
    return 1;
×
142
  else
143
    return 2;
9✔
144
}
20✔
145

146
////////////////////////////////////////////////////////////////////////////////
147
int CmdConfig::execute(std::string& output) {
769✔
148
  auto rc = 0;
769✔
149
  std::stringstream out;
769✔
150

151
  // Get the non-attribute, non-fancy command line arguments.
152
  std::vector<std::string> words = Context::getContext().cli2.getWords();
769✔
153

154
  // Support:
155
  //   task config name value    # set name to value
156
  //   task config name ""       # set name to blank
157
  //   task config name          # remove name
158
  if (words.size()) {
769✔
159
    auto confirmation = Context::getContext().config.getBoolean("confirmation");
768✔
160
    auto found = false;
768✔
161

162
    auto name = words[0];
768✔
163
    std::string value = "";
768✔
164

165
    // Join the remaining words into config variable's value
166
    if (words.size() > 1) {
768✔
167
      for (unsigned int i = 1; i < words.size(); ++i) {
1,526✔
168
        if (i > 1) value += ' ';
763✔
169

170
        value += words[i];
763✔
171
      }
172
    }
173

174
    if (name != "") {
768✔
175
      auto change = false;
768✔
176

177
      // task config name value
178
      // task config name ""
179
      if (words.size() > 1) change = setConfigVariable(name, value, confirmation);
768✔
180

181
      // task config name
182
      else {
183
        rc = unsetConfigVariable(name, confirmation);
5✔
184
        if (rc == 0) {
5✔
185
          change = true;
4✔
186
          found = true;
4✔
187
        } else if (rc == 1)
1✔
UNCOV
188
          found = true;
×
189

190
        if (!found) throw format("No entry named '{1}' found.", name);
5✔
191
      }
192

193
      // Show feedback depending on whether .taskrc has been rewritten
194
      if (change) {
767✔
195
        out << format("Config file {1} modified.", Context::getContext().config.file()) << '\n';
767✔
196
      } else
UNCOV
197
        out << "No changes made.\n";
×
198
    } else
NEW
199
      throw std::string("Specify the name of a config variable to modify.");
×
200

201
    output = out.str();
767✔
202
  } else
769✔
203
    throw std::string("Specify the name of a config variable to modify.");
1✔
204

205
  return rc;
767✔
206
}
771✔
207

208
////////////////////////////////////////////////////////////////////////////////
209
CmdCompletionConfig::CmdCompletionConfig() {
4,389✔
210
  _keyword = "_config";
4,389✔
211
  _usage = "task          _config";
4,389✔
212
  _description = "Lists all supported configuration variables, for completion purposes";
4,389✔
213
  _read_only = true;
4,389✔
214
  _displays_id = false;
4,389✔
215
  _needs_gc = false;
4,389✔
216
  _uses_context = false;
4,389✔
217
  _accepts_filter = false;
4,389✔
218
  _accepts_modifications = false;
4,389✔
219
  _accepts_miscellaneous = false;
4,389✔
220
  _category = Command::Category::internal;
4,389✔
221
}
4,389✔
222

223
////////////////////////////////////////////////////////////////////////////////
224
int CmdCompletionConfig::execute(std::string& output) {
1✔
225
  auto configs = Context::getContext().config.all();
1✔
226
  std::sort(configs.begin(), configs.end());
1✔
227

228
  for (const auto& config : configs) output += config + '\n';
246✔
229

230
  return 0;
1✔
231
}
1✔
232

233
////////////////////////////////////////////////////////////////////////////////
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc