diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ec2695..d73ef72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ Cytoplasm. It is intended to be updated with every commit that makes a user-faci change worth reporting in the change log. As such, it changes frequently between releases. Final change log entries are published as [Releases](releases). +## v0.4.1 + +### Tools + +#### `j2s` + +- Added an option to allow additional fields in structures and ignore them in +encoding and decoding. Note that additional fields are totally untouched—they +are not even initialized to a default value. + ## v0.4.0 **Released on November 1, 2023** diff --git a/tools/j2s.c b/tools/j2s.c index 9a85a00..674d6d3 100644 --- a/tools/j2s.c +++ b/tools/j2s.c @@ -309,22 +309,23 @@ Main(Array * args) ArrayAdd(requiredTypes, StrDuplicate(type)); } - typeFieldsVal = HashMapGet(typeObj, "fields"); - if (JsonValueType(typeFieldsVal) != JSON_OBJECT) - { - Log(LOG_ERR, "Validation error: 'types.%s.fields' must be an object.", type); - goto finish; - } - - typeFields = JsonValueAsObject(typeFieldsVal); - if (StrEquals(typeType, "struct")) { + typeFieldsVal = HashMapGet(typeObj, "fields"); + if (JsonValueType(typeFieldsVal) != JSON_OBJECT) + { + Log(LOG_ERR, "Validation error: 'types.%s.fields' must be an object.", type); + goto finish; + } + + typeFields = JsonValueAsObject(typeFieldsVal); + while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal)) { char *fieldType; int isArrType = 0; JsonValue *requiredVal; + JsonValue *ignoreVal; if (JsonValueType(fieldVal) != JSON_OBJECT) { @@ -379,10 +380,26 @@ Main(Array * args) Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.required' must be a boolean.", type, fieldName); goto finish; } + + ignoreVal = HashMapGet(fieldObj, "ignore"); + if (ignoreVal && JsonValueType(ignoreVal) != JSON_BOOLEAN) + { + Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.ignore' must be a boolean.", type, fieldName); + goto finish; + } } } else if (StrEquals(typeType, "enum")) { + typeFieldsVal = HashMapGet(typeObj, "fields"); + if (JsonValueType(typeFieldsVal) != JSON_OBJECT) + { + Log(LOG_ERR, "Validation error: 'types.%s.fields' must be an object.", type); + goto finish; + } + + typeFields = JsonValueAsObject(typeFieldsVal); + while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal)) { char *name; @@ -403,16 +420,17 @@ Main(Array * args) } } } + else if (StrEquals(typeType, "extern")) + { + /* + * No code will be generated for this type. We simply assume that it exists. + */ + } else { Log(LOG_ERR, "Validation error: 'types.%s.type' must be 'struct' or 'enum'.", type); goto finish; } - /* - * TODO: Add "extern" type that doesn't actually generate any code, - * but trusts the user that it has been generated somewhere else. This - * is effectively "importing" types. - */ } sortedNodes = GraphTopologicalSort(dependencyGraph, &sortedNodesLen); @@ -471,6 +489,12 @@ Main(Array * args) } typeType = JsonValueAsString(JsonGet(types, 2, type, "type")); + + if (StrEquals(typeType, "extern")) + { + continue; + } + fields = JsonValueAsObject(JsonGet(types, 2, type, "fields")); StreamPrintf(headerFile, "typedef %s %s\n{\n", typeType, type); @@ -615,11 +639,18 @@ Main(Array * args) { char *key = ArrayGet(keys, i); int required = JsonValueAsBoolean(JsonGet(fields, 2, key, "required")); + int ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore")); char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type")); int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum"); JsonType jsonType = isEnum ? JSON_STRING : TypeToJsonType(fieldType); char *jsonTypeStr = JsonTypeToStr(jsonType); + if (ignore) + { + StreamPrintf(implFile, " /* Ignored field: %s */\n\n", key); + continue; + } + StreamPrintf(implFile, " val = HashMapGet(json, \"%s\");\n", Trim('_', key)); StreamPrintf(implFile, " if (val)\n {\n"); @@ -847,6 +878,13 @@ Main(Array * args) char *key = ArrayGet(keys, i); char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type")); int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum"); + int ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore")); + + if (ignore) + { + StreamPrintf(implFile, " /* Ignored field: %s */\n\n", key); + continue; + } if (StrEquals(fieldType, "array")) { @@ -1022,8 +1060,9 @@ Main(Array * args) else { /* Ignore primitives but call the appropriate free - * method on declared types */ - if (!isEnum && HashMapGet(types, fieldType)) + * method on declared types that aren't "extern". */ + char *fieldTypeType = JsonValueAsString(JsonGet(types, 2, fieldType, "type")); + if (!isEnum && HashMapGet(types, fieldType) && !StrEquals(fieldTypeType, "extern")) { StreamPrintf(implFile, " %sFree(&val->%s);\n", fieldType, key); }