Skip to content

Commit b52c1da

Browse files
committed
make id required
1 parent 703b359 commit b52c1da

File tree

5 files changed

+47
-124
lines changed

5 files changed

+47
-124
lines changed

examples/comp_options/id/README.md

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,6 @@ You can use the defined ids as variable names in commands.
2222
}
2323
```
2424

25-
## Undefined IDs
26-
27-
> [!warning]
28-
> Undifined ids are deprecated. Please define an id for each component.
29-
30-
When you put an undefined id in `%*%`, it'll use one of the components that have no id.
31-
32-
```json
33-
{
34-
"command": "echo x: %-% & echo y: %y% & echo z: %foo%",
35-
"button": "Echo!",
36-
"components": [
37-
{
38-
"type": "text",
39-
"label": "Value of y",
40-
"id": "y"
41-
},
42-
{
43-
"type": "text",
44-
"label": "Value of x"
45-
},
46-
{
47-
"type": "text",
48-
"label": "Value of z"
49-
}
50-
]
51-
}
52-
```
53-
5425
## Predefined IDs
5526

5627
There are some predefined ids.

examples/comp_options/id/gui_definition.json

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,6 @@
1717
}
1818
]
1919
},
20-
{
21-
"label": "Undefined IDs",
22-
"command": "echo x: %-% & echo y: %y% & echo z: %foo%",
23-
"button": "Echo!",
24-
"components": [
25-
{
26-
"type": "text",
27-
"label": "Value of y",
28-
"id": "y"
29-
},
30-
{
31-
"type": "text",
32-
"label": "Value of x"
33-
},
34-
{
35-
"type": "text",
36-
"label": "Value of z"
37-
}
38-
]
39-
},
4020
{
4121
"label": "Reserved IDs",
4222
"command": "echo percent: %% & echo cwd: %__CWD__% & echo home: %__HOME__%",

src/json_utils.cpp

Lines changed: 32 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,9 @@ static void CompileCommand(noex::string& err_msg,
242242
splitted_cmd_json.SetArray();
243243

244244
bool store_ids = false;
245-
const char* cmd = sub_definition["command"].GetString();
245+
tuwjson::Value& cmd_json = sub_definition["command"];
246+
noex::string cmd_pos = cmd_json.GetLineColumnStr();
247+
const char* cmd = cmd_json.GetString();
246248
while (*cmd != '\0') {
247249
noex::string token = SubstrToChar(cmd, '%');
248250
if (store_ids) {
@@ -263,52 +265,32 @@ static void CompileCommand(noex::string& err_msg,
263265
tuwjson::Value& components = sub_definition["components"];
264266
tuwjson::Value cmd_int_ids;
265267
cmd_int_ids.SetArray();
266-
noex::string cmd_str;
267268
int comp_size = static_cast<int>(comp_ids.size());
268269
int non_id_comp = 0;
269270
for (int i = 0; i < static_cast<int>(cmd_ids.size()); i++) {
270-
cmd_str += splitted_cmd[i];
271271
const noex::string& id = cmd_ids[i];
272272
int j;
273273
if (id == CMD_TOKEN_PERCENT) {
274274
j = CMD_ID_PERCENT;
275-
cmd_str.push_back('%');
276275
} else if (id == CMD_TOKEN_CURRENT_DIR) {
277276
j = CMD_ID_CURRENT_DIR;
278-
cmd_str += id;
279277
} else if (id == CMD_TOKEN_HOME_DIR) {
280278
j = CMD_ID_HOME_DIR;
281-
cmd_str += id;
282279
} else {
283280
for (j = 0; j < comp_size; j++)
284281
if (id == comp_ids[j]) break;
285282
if (j == comp_size) {
286-
while (non_id_comp < comp_size) {
287-
int type_int = components[non_id_comp]["type_int"].GetInt();
288-
if (type_int != COMP_STATIC_TEXT
289-
&& type_int != COMP_EMPTY
290-
&& comp_ids[non_id_comp].empty())
291-
break;
292-
non_id_comp++;
293-
}
294-
j = non_id_comp;
295-
non_id_comp++;
283+
err_msg = noex::concat_cstr(
284+
"There is undefined id \"", id.c_str(), "\" in the command.") + cmd_pos;
285+
return;
296286
}
297287
}
298-
if (j < comp_size) {
299-
tuwjson::Value n;
300-
n.SetInt(j);
301-
cmd_int_ids.MoveAndPush(n);
302-
}
303-
if (j >= comp_size)
304-
cmd_str += "__comp???__";
305-
else if (j >= 0)
306-
cmd_str += noex::concat_cstr("__comp", noex::to_string(j).c_str(), "__");
288+
tuwjson::Value n;
289+
n.SetInt(j);
290+
cmd_int_ids.MoveAndPush(n);
307291
}
308-
if (cmd_ids.size() < splitted_cmd.size())
309-
cmd_str += splitted_cmd.back();
310292

311-
// Check if the command requires more arguments or ignores some arguments.
293+
// Find unused IDs.
312294
for (int j = 0; j < comp_size; j++) {
313295
tuwjson::Value& v = components[j];
314296
int type_int = v["type_int"].GetInt();
@@ -321,25 +303,14 @@ static void CompileCommand(noex::string& err_msg,
321303
break;
322304
}
323305
if (!found) {
324-
if (comp_ids[j].empty() || !v.HasMember("id")) {
325-
err_msg = noex::concat_cstr("[\"components\"][", noex::to_string(j).c_str(), "]")
326-
+ v.GetLineColumnStr() + " is unused in the command; " + cmd_str;
327-
} else {
328-
tuwjson::Value& vid = v["id"];
329-
err_msg = noex::concat_cstr("component id \"", vid.GetString(), "\"")
330-
+ vid.GetLineColumnStr() + " is unused in the command; " + cmd_str;
331-
}
306+
tuwjson::Value& vid = v["id"];
307+
err_msg = noex::concat_cstr("component id \"", vid.GetString(), "\"")
308+
+ noex::concat_cstr(
309+
vid.GetLineColumnStr().c_str(),
310+
" is unused in the command.", cmd_pos.c_str());
332311
return;
333312
}
334313
}
335-
if (non_id_comp > comp_size) {
336-
err_msg =
337-
"The command requires more components for arguments; " + cmd_str;
338-
return;
339-
}
340-
tuwjson::Value v;
341-
v.SetString(cmd_str);
342-
sub_definition["command_str"].MoveFrom(v);
343314
sub_definition["command_ids"].MoveFrom(cmd_int_ids);
344315
}
345316

@@ -443,14 +414,9 @@ void CheckSubDefinition(noex::string& err_msg, tuwjson::Value& sub_definition,
443414
const char* type_str = type_ptr->GetString();
444415
int type = ComptypeToInt(type_str);
445416

446-
// TODO: throw an error for missing ids.
447-
if (type != COMP_STATIC_TEXT && !c.HasMember("id")) {
448-
PrintFmt(
449-
"[CheckDefinition] DeprecationWarning: "
450-
"\"id\" is missing in [\"components\"][%zu]%s."
451-
" Support for components without \"id\" will be removed in a future version.\n",
452-
comp_ids.size(), c.GetLineColumnStr().c_str());
453-
}
417+
tuwjson::Value* id_ptr = CheckJsonType(
418+
err_msg, c, "id", JsonType::STRING, "component", type == COMP_STATIC_TEXT);
419+
if (!err_msg.empty()) return;
454420

455421
c["type_int"].SetInt(type);
456422
CorrectKey(c, "item", "items");
@@ -549,7 +515,6 @@ void CheckSubDefinition(noex::string& err_msg, tuwjson::Value& sub_definition,
549515
CheckJsonType(err_msg, c, "add_quotes", JsonType::BOOLEAN);
550516
CorrectKey(c, "empty_message", "placeholder");
551517
CheckJsonType(err_msg, c, "placeholder", JsonType::STRING);
552-
CheckJsonType(err_msg, c, "id", JsonType::STRING);
553518
CheckJsonType(err_msg, c, "tooltip", JsonType::STRING);
554519

555520
CheckJsonType(err_msg, c, "optional", JsonType::BOOLEAN);
@@ -571,16 +536,22 @@ void CheckSubDefinition(noex::string& err_msg, tuwjson::Value& sub_definition,
571536
}
572537
}
573538

574-
const char* id = GetString(c, "id", "");
575-
if (c.HasMember("id")) {
576-
noex::string linecol = c["id"].GetLineColumnStr();
539+
if (id_ptr != nullptr) {
540+
const char* id = id_ptr->GetString();
541+
noex::string linecol = id_ptr->GetLineColumnStr();
577542
if (id[0] == '\0') {
578543
err_msg = "\"id\" should NOT be an empty string."
579544
+ linecol;
580545
} else if (id[0] == '_') {
581546
err_msg = "\"id\" should NOT start with '_'."
582547
+ linecol;
583548
}
549+
if (type == COMP_STATIC_TEXT) {
550+
id_ptr->SetString("");
551+
PrintFmt(
552+
"[CheckDefinition] Warning: static text should not have ID. %s\n",
553+
linecol.c_str());
554+
}
584555
if (!ignore) {
585556
for (const noex::string& str : comp_ids) {
586557
if (id == str) {
@@ -590,26 +561,25 @@ void CheckSubDefinition(noex::string& err_msg, tuwjson::Value& sub_definition,
590561
}
591562
}
592563
}
593-
} else {
594-
uint32_t hash = Fnv1Hash32(c["label"].GetString());
595-
c["id"].SetString("_" + noex::to_string(hash));
596564
}
597565
if (!err_msg.empty()) return;
598566

599567
if (ignore) {
600568
comp_ids.emplace_back("");
601569
c["type_int"].SetInt(COMP_EMPTY);
570+
} else if (type == COMP_STATIC_TEXT) {
571+
comp_ids.emplace_back("");
602572
} else {
603-
comp_ids.emplace_back(id);
573+
comp_ids.emplace_back(id_ptr->GetString());
604574
}
605575
}
606576

607577
// Overwrite ["command"] with ["command_'os'"] if exists.
608578
const char* command_os_key = "command_" TUW_CONSTANTS_OS;
609579
json_ptr = CheckJsonType(err_msg, sub_definition, command_os_key, JsonType::STRING);
610580
if (err_msg.empty() && json_ptr) {
611-
const char* command_os = json_ptr->GetString();
612-
sub_definition["command"].SetString(command_os);
581+
// Note: CopyFrom() can overwrite m_line_count and m_column for error messages.
582+
sub_definition["command"].CopyFrom(*json_ptr);
613583
}
614584

615585
// check sub_definition["command"] and convert it to more useful format.

src/main_frame.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ void MainFrame::UpdatePanel(size_t definition_id) noexcept {
341341
const char* label = sub_definition["label"].GetString();
342342
Log("UpdatePanel", "Label", label);
343343
}
344-
const char* cmd_str = sub_definition["command_str"].GetString();
344+
const char* cmd_str = sub_definition["command"].GetString();
345345
Log("UpdatePanel", "Command", cmd_str);
346346
const char* window_name = json_utils::GetString(sub_definition,
347347
"window_name", tuw_constants::TOOL_NAME);

tests/json_check_test.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,30 +123,32 @@ TEST(JsonCheckTest, checkGUIFail6) {
123123
GetTestJson(test_json);
124124
test_json["gui"][0]["components"].GetArray()->pop_back();
125125
CheckGUIError(test_json,
126-
"The command requires more components for arguments;"
127-
" echo file: __comp1__ & echo folder: __comp2__ & echo combo: __comp3__ &"
128-
" echo radio: __comp4__ & echo check: __comp5__ & echo check_array: __comp6__ &"
129-
" echo textbox: __comp7__ & echo int: __comp8__ & echo float: __comp???__");
126+
"There is undefined id \"double\" in the command. (line: 7, column: 24)");
130127
}
131128

132129
TEST(JsonCheckTest, checkGUIFail7) {
133130
tuwjson::Value test_json;
134131
GetTestJson(test_json);
135-
test_json["gui"][0]["components"][1]["id"].SetString("aaa");
132+
test_json["gui"][0]["command"].SetString("echo hello");
136133
CheckGUIError(test_json,
137-
"component id \"aaa\" (line: 15, column: 27) is unused in the command;"
138-
" echo file: __comp???__ & echo folder: __comp2__ & echo combo: __comp3__"
139-
" & echo radio: __comp4__ & echo check: __comp5__ & echo check_array: __comp6__"
140-
" & echo textbox: __comp7__ & echo int: __comp8__ & echo float: __comp9__");
134+
"component id \"file\" (line: 15, column: 27) is unused in the command."
135+
" (line: 7, column: 24)");
141136
}
142137

143138
TEST(JsonCheckTest, checkGUIFail8) {
144139
tuwjson::Value test_json;
145140
GetTestJson(test_json);
146-
test_json["gui"][0]["components"][0]["id"].SetString("aaa");
147-
test_json["gui"][0]["components"][1]["id"].SetString("aaa");
141+
test_json["gui"][0]["components"][0].CopyFrom(test_json["gui"][0]["components"][1]);
148142
CheckGUIError(test_json,
149-
"Found a duplicated id: \"aaa\" (line: 15, column: 27)");
143+
"Found a duplicated id: \"file\" (line: 15, column: 27)");
144+
}
145+
146+
TEST(JsonCheckTest, checkGUIFail9) {
147+
tuwjson::Value test_json;
148+
GetTestJson(test_json);
149+
test_json["gui"][0]["components"][1].ReplaceKey("id", "id_");
150+
CheckGUIError(test_json,
151+
"component requires \"id\" (line: 13, column: 17)");
150152
}
151153

152154
TEST(JsonCheckTest, checkGUIFailRelaxed) {

0 commit comments

Comments
 (0)