@redpanda-data/docs-extensions-and-macros 4.10.0 → 4.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/doc-tools.js
CHANGED
|
@@ -1111,6 +1111,8 @@ automation
|
|
|
1111
1111
|
} else {
|
|
1112
1112
|
env.OUTPUT_JSON_DIR = path.resolve(outputDir, 'examples');
|
|
1113
1113
|
env.OUTPUT_AUTOGENERATED_DIR = path.resolve(outputDir);
|
|
1114
|
+
// Set property files to go to properties subdirectory
|
|
1115
|
+
env.OUTPUT_ASCIIDOC_DIR = path.resolve(outputDir, 'pages', 'properties');
|
|
1114
1116
|
}
|
|
1115
1117
|
|
|
1116
1118
|
const r = spawnSync('make', args, { cwd, stdio: 'inherit', env });
|
package/package.json
CHANGED
|
@@ -111,10 +111,10 @@ generate-docs: node-deps
|
|
|
111
111
|
@# Use the enhanced properties file (with overrides) for documentation generation if it exists
|
|
112
112
|
@if [ -f "$(TOOL_ROOT)/gen/$(TAG)-properties.json" ]; then \
|
|
113
113
|
cd $(TOOL_ROOT) && \
|
|
114
|
-
node generate-handlebars-docs.js "gen/$(TAG)-properties.json" "$(
|
|
114
|
+
node generate-handlebars-docs.js "gen/$(TAG)-properties.json" "$(OUTPUT_ASCIIDOC_DIR)"; \
|
|
115
115
|
else \
|
|
116
116
|
cd $(TOOL_ROOT) && \
|
|
117
|
-
node generate-handlebars-docs.js "gen/properties-output.json" "$(
|
|
117
|
+
node generate-handlebars-docs.js "gen/properties-output.json" "$(OUTPUT_ASCIIDOC_DIR)"; \
|
|
118
118
|
fi
|
|
119
119
|
@echo "📄 Copying properties JSON files to $(OUTPUT_JSON_DIR)…"
|
|
120
120
|
@if [ -f "$(TOOL_ROOT)/gen/$(TAG)-properties.json" ]; then \
|
|
@@ -60,7 +60,17 @@ NOTE: Some cluster properties require that you restart the cluster for any updat
|
|
|
60
60
|
sectionTitle: 'Cluster configuration',
|
|
61
61
|
groups: [
|
|
62
62
|
{
|
|
63
|
-
filter: (prop) => prop.config_scope === 'cluster' && !prop.is_deprecated
|
|
63
|
+
filter: (prop) => prop.config_scope === 'cluster' && !prop.is_deprecated && !(
|
|
64
|
+
prop.name && (
|
|
65
|
+
prop.name.includes('cloud_storage') ||
|
|
66
|
+
prop.name.includes('s3_') ||
|
|
67
|
+
prop.name.includes('azure_') ||
|
|
68
|
+
prop.name.includes('gcs_') ||
|
|
69
|
+
prop.name.includes('archival_') ||
|
|
70
|
+
prop.name.includes('remote_') ||
|
|
71
|
+
prop.name.includes('tiered_')
|
|
72
|
+
)
|
|
73
|
+
)
|
|
64
74
|
}
|
|
65
75
|
],
|
|
66
76
|
filename: 'cluster-properties.adoc'
|
|
@@ -269,7 +279,8 @@ function generateDeprecatedDocs(properties, outputDir) {
|
|
|
269
279
|
};
|
|
270
280
|
|
|
271
281
|
const output = template(data);
|
|
272
|
-
|
|
282
|
+
// Navigate back from pages/properties to reference, then into partials/deprecated
|
|
283
|
+
const outputPath = path.join(path.dirname(path.dirname(outputDir)), 'partials', 'deprecated', 'deprecated-properties.adoc');
|
|
273
284
|
|
|
274
285
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
275
286
|
fs.writeFileSync(outputPath, output, 'utf8');
|
|
@@ -332,7 +343,7 @@ function generateAllDocs(inputFile, outputDir) {
|
|
|
332
343
|
|
|
333
344
|
// Generate each type of documentation
|
|
334
345
|
for (const [type, config] of Object.entries(PROPERTY_CONFIG)) {
|
|
335
|
-
const count = generatePropertyDocs(properties, config,
|
|
346
|
+
const count = generatePropertyDocs(properties, config, outputDir);
|
|
336
347
|
totalProperties += count;
|
|
337
348
|
|
|
338
349
|
if (type === 'broker') totalBrokerProperties = count;
|
|
@@ -342,7 +353,7 @@ function generateAllDocs(inputFile, outputDir) {
|
|
|
342
353
|
}
|
|
343
354
|
|
|
344
355
|
// Generate deprecated properties documentation
|
|
345
|
-
const deprecatedCount = generateDeprecatedDocs(properties,
|
|
356
|
+
const deprecatedCount = generateDeprecatedDocs(properties, outputDir);
|
|
346
357
|
|
|
347
358
|
// Generate summary file
|
|
348
359
|
const allPropertiesContent = Object.keys(properties).sort().join('\n');
|
|
@@ -117,6 +117,24 @@ def process_enterprise_value(enterprise_str):
|
|
|
117
117
|
"""
|
|
118
118
|
enterprise_str = enterprise_str.strip()
|
|
119
119
|
|
|
120
|
+
# Handle special SASL mechanism function names
|
|
121
|
+
if enterprise_str == "is_enterprise_sasl_mechanism":
|
|
122
|
+
# Dynamically look up enterprise SASL mechanisms from source
|
|
123
|
+
enterprise_mechanisms = get_enterprise_sasl_mechanisms()
|
|
124
|
+
if enterprise_mechanisms:
|
|
125
|
+
return enterprise_mechanisms
|
|
126
|
+
else:
|
|
127
|
+
# Fallback to known values if lookup fails
|
|
128
|
+
return ["GSSAPI", "OAUTHBEARER"]
|
|
129
|
+
elif enterprise_str == "is_enterprise_sasl_mechanisms_override":
|
|
130
|
+
# Get the enterprise mechanisms dynamically for a more accurate description
|
|
131
|
+
enterprise_mechanisms = get_enterprise_sasl_mechanisms()
|
|
132
|
+
if enterprise_mechanisms:
|
|
133
|
+
mechanism_list = ", ".join(enterprise_mechanisms)
|
|
134
|
+
return f"Any override containing enterprise mechanisms ({mechanism_list})."
|
|
135
|
+
else:
|
|
136
|
+
return "Any override containing enterprise mechanisms."
|
|
137
|
+
|
|
120
138
|
# FIRST: Handle std::vector initialization patterns (highest priority)
|
|
121
139
|
# This must come before enum processing because vectors can contain enums
|
|
122
140
|
# Tolerate optional whitespace around braces
|
|
@@ -196,19 +214,15 @@ def process_enterprise_value(enterprise_str):
|
|
|
196
214
|
|
|
197
215
|
def resolve_cpp_function_call(function_name):
|
|
198
216
|
"""
|
|
199
|
-
Resolve
|
|
217
|
+
Resolve a small set of known zero-argument C++ functions to their literal string return values by scanning a local Redpanda source tree.
|
|
200
218
|
|
|
201
|
-
|
|
219
|
+
Searches predefined files and regex patterns for the specified fully-qualified function name (e.g., "model::kafka_audit_logging_topic") and returns the captured string if found; returns None when no match or when the Redpanda source tree cannot be located.
|
|
202
220
|
|
|
203
221
|
Parameters:
|
|
204
|
-
function_name (str): Fully-qualified C++ function name to resolve
|
|
222
|
+
function_name (str): Fully-qualified C++ function name to resolve.
|
|
205
223
|
|
|
206
224
|
Returns:
|
|
207
|
-
str or None: The
|
|
208
|
-
|
|
209
|
-
Notes:
|
|
210
|
-
- The function performs filesystem I/O and regex-based source searching; it does not raise on read errors but logs and continues.
|
|
211
|
-
- Only a small, hard-coded set of function names/patterns is supported; unknown names immediately return None.
|
|
225
|
+
str or None: The literal string returned by the C++ function when resolved, or `None` if unresolved.
|
|
212
226
|
"""
|
|
213
227
|
# Map function names to likely search patterns and file locations
|
|
214
228
|
search_patterns = {
|
|
@@ -322,7 +336,174 @@ def resolve_cpp_function_call(function_name):
|
|
|
322
336
|
return None
|
|
323
337
|
|
|
324
338
|
|
|
339
|
+
def resolve_constexpr_identifier(identifier):
|
|
340
|
+
"""
|
|
341
|
+
Resolve a constexpr identifier from Redpanda source code to its literal string value.
|
|
342
|
+
|
|
343
|
+
Searches common Redpanda source locations for constexpr string or string_view definitions matching the given identifier and returns the literal if found.
|
|
344
|
+
|
|
345
|
+
Parameters:
|
|
346
|
+
identifier (str): The identifier name to resolve (e.g., "scram").
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
str or None: The resolved literal string value if found, otherwise `None`.
|
|
350
|
+
"""
|
|
351
|
+
# Try to find the Redpanda source directory
|
|
352
|
+
redpanda_source_paths = [
|
|
353
|
+
'tmp/redpanda', # Current directory
|
|
354
|
+
'../tmp/redpanda', # Parent directory
|
|
355
|
+
'tools/property-extractor/tmp/redpanda', # From project root
|
|
356
|
+
os.path.join(os.getcwd(), 'tools', 'property-extractor', 'tmp', 'redpanda')
|
|
357
|
+
]
|
|
358
|
+
|
|
359
|
+
redpanda_source = None
|
|
360
|
+
for path in redpanda_source_paths:
|
|
361
|
+
if os.path.exists(path):
|
|
362
|
+
redpanda_source = path
|
|
363
|
+
break
|
|
364
|
+
|
|
365
|
+
if not redpanda_source:
|
|
366
|
+
logger.debug(f"Could not find Redpanda source directory to resolve identifier: {identifier}")
|
|
367
|
+
return None
|
|
368
|
+
|
|
369
|
+
# Pattern to match constexpr string_view definitions
|
|
370
|
+
# Matches: inline constexpr std::string_view scram{"SCRAM"};
|
|
371
|
+
patterns = [
|
|
372
|
+
rf'inline\s+constexpr\s+std::string_view\s+{re.escape(identifier)}\s*\{{\s*"([^"]+)"\s*\}}',
|
|
373
|
+
rf'constexpr\s+std::string_view\s+{re.escape(identifier)}\s*\{{\s*"([^"]+)"\s*\}}',
|
|
374
|
+
rf'inline\s+constexpr\s+auto\s+{re.escape(identifier)}\s*=\s*"([^"]+)"',
|
|
375
|
+
rf'constexpr\s+auto\s+{re.escape(identifier)}\s*=\s*"([^"]+)"',
|
|
376
|
+
rf'static\s+constexpr\s+std::string_view\s+{re.escape(identifier)}\s*\{{\s*"([^"]+)"\s*\}}',
|
|
377
|
+
rf'static\s+inline\s+constexpr\s+std::string_view\s+{re.escape(identifier)}\s*\{{\s*"([^"]+)"\s*\}}',
|
|
378
|
+
]
|
|
379
|
+
|
|
380
|
+
# Search recursively through the config directory and other common locations
|
|
381
|
+
search_dirs = [
|
|
382
|
+
os.path.join(redpanda_source, 'src', 'v', 'config'),
|
|
383
|
+
os.path.join(redpanda_source, 'src', 'v', 'kafka'),
|
|
384
|
+
os.path.join(redpanda_source, 'src', 'v', 'security'),
|
|
385
|
+
os.path.join(redpanda_source, 'src', 'v', 'pandaproxy'),
|
|
386
|
+
]
|
|
387
|
+
|
|
388
|
+
for search_dir in search_dirs:
|
|
389
|
+
if not os.path.exists(search_dir):
|
|
390
|
+
continue
|
|
391
|
+
|
|
392
|
+
# Walk through the directory recursively
|
|
393
|
+
for root, dirs, files in os.walk(search_dir):
|
|
394
|
+
for file in files:
|
|
395
|
+
# Check both .h and .cc files since definitions can be in either
|
|
396
|
+
if file.endswith(('.h', '.cc', '.hpp', '.cpp')):
|
|
397
|
+
file_path = os.path.join(root, file)
|
|
398
|
+
try:
|
|
399
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
400
|
+
content = f.read()
|
|
401
|
+
|
|
402
|
+
# Try each pattern
|
|
403
|
+
for pattern in patterns:
|
|
404
|
+
match = re.search(pattern, content, re.MULTILINE)
|
|
405
|
+
if match:
|
|
406
|
+
resolved_value = match.group(1)
|
|
407
|
+
logger.debug(f"Resolved identifier '{identifier}' -> '{resolved_value}' from {file_path}")
|
|
408
|
+
return resolved_value
|
|
409
|
+
|
|
410
|
+
except (FileNotFoundError, PermissionError, OSError, UnicodeDecodeError) as e:
|
|
411
|
+
logger.debug(f"Error reading {file_path}: {e}")
|
|
412
|
+
continue
|
|
413
|
+
|
|
414
|
+
logger.debug(f"Could not resolve identifier: {identifier}")
|
|
415
|
+
return None
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def get_enterprise_sasl_mechanisms():
|
|
419
|
+
"""
|
|
420
|
+
Locate and resolve enterprise SASL mechanisms declared in Redpanda's sasl_mechanisms.h.
|
|
421
|
+
|
|
422
|
+
Searches known Redpanda source locations for an inline constexpr definition of enterprise_sasl_mechanisms,
|
|
423
|
+
extracts the identifiers, and resolves each identifier to its literal string value where possible; unresolved
|
|
424
|
+
identifiers are converted to an uppercase fallback.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
list or None: List of enterprise SASL mechanism strings (e.g., ["GSSAPI", "OAUTHBEARER"]),
|
|
428
|
+
or `None` if the lookup fails.
|
|
429
|
+
"""
|
|
430
|
+
# Try to find the Redpanda source directory
|
|
431
|
+
redpanda_source_paths = [
|
|
432
|
+
'tmp/redpanda', # Current directory
|
|
433
|
+
'../tmp/redpanda', # Parent directory
|
|
434
|
+
'tools/property-extractor/tmp/redpanda', # From project root
|
|
435
|
+
os.path.join(os.getcwd(), 'tools', 'property-extractor', 'tmp', 'redpanda')
|
|
436
|
+
]
|
|
437
|
+
|
|
438
|
+
redpanda_source = None
|
|
439
|
+
for path in redpanda_source_paths:
|
|
440
|
+
if os.path.exists(path):
|
|
441
|
+
redpanda_source = path
|
|
442
|
+
break
|
|
443
|
+
|
|
444
|
+
if not redpanda_source:
|
|
445
|
+
logger.debug("Could not find Redpanda source directory to resolve enterprise SASL mechanisms")
|
|
446
|
+
return None
|
|
447
|
+
|
|
448
|
+
# Look for the enterprise_sasl_mechanisms definition in sasl_mechanisms.h
|
|
449
|
+
sasl_mechanisms_file = os.path.join(redpanda_source, 'src', 'v', 'config', 'sasl_mechanisms.h')
|
|
450
|
+
|
|
451
|
+
if not os.path.exists(sasl_mechanisms_file):
|
|
452
|
+
logger.debug(f"sasl_mechanisms.h not found at {sasl_mechanisms_file}")
|
|
453
|
+
return None
|
|
454
|
+
|
|
455
|
+
try:
|
|
456
|
+
with open(sasl_mechanisms_file, 'r', encoding='utf-8') as f:
|
|
457
|
+
content = f.read()
|
|
458
|
+
|
|
459
|
+
# Pattern to match the enterprise_sasl_mechanisms array definition
|
|
460
|
+
# inline constexpr auto enterprise_sasl_mechanisms = std::to_array<std::string_view>({gssapi, oauthbearer});
|
|
461
|
+
pattern = r'inline\s+constexpr\s+auto\s+enterprise_sasl_mechanisms\s*=\s*std::to_array<[^>]+>\s*\(\s*\{\s*([^}]+)\s*\}\s*\)'
|
|
462
|
+
|
|
463
|
+
match = re.search(pattern, content, re.MULTILINE | re.DOTALL)
|
|
464
|
+
if match:
|
|
465
|
+
# Extract the identifiers from the array (e.g., "gssapi, oauthbearer")
|
|
466
|
+
identifiers_str = match.group(1).strip()
|
|
467
|
+
|
|
468
|
+
# Split by comma and clean up whitespace
|
|
469
|
+
identifiers = [id.strip() for id in identifiers_str.split(',') if id.strip()]
|
|
470
|
+
|
|
471
|
+
# Resolve each identifier to its actual string value
|
|
472
|
+
mechanisms = []
|
|
473
|
+
for identifier in identifiers:
|
|
474
|
+
resolved_value = resolve_constexpr_identifier(identifier)
|
|
475
|
+
if resolved_value:
|
|
476
|
+
mechanisms.append(resolved_value)
|
|
477
|
+
else:
|
|
478
|
+
logger.debug(f"Could not resolve SASL mechanism identifier: {identifier}")
|
|
479
|
+
# Fallback: use the identifier name in uppercase
|
|
480
|
+
mechanisms.append(identifier.upper())
|
|
481
|
+
|
|
482
|
+
if mechanisms:
|
|
483
|
+
logger.debug(f"Resolved enterprise SASL mechanisms: {mechanisms}")
|
|
484
|
+
return mechanisms
|
|
485
|
+
else:
|
|
486
|
+
logger.debug("Could not find enterprise_sasl_mechanisms definition in sasl_mechanisms.h")
|
|
487
|
+
return None
|
|
488
|
+
|
|
489
|
+
except (OSError, UnicodeDecodeError, re.error) as e:
|
|
490
|
+
logger.debug(f"Error reading {sasl_mechanisms_file}: {e}")
|
|
491
|
+
return None
|
|
492
|
+
|
|
493
|
+
|
|
325
494
|
def validate_paths(options):
|
|
495
|
+
"""
|
|
496
|
+
Validate that required file-system paths referenced by `options` exist and exit the process on failure.
|
|
497
|
+
|
|
498
|
+
Checks:
|
|
499
|
+
- Verifies `options.path` exists; logs an error and exits with status code 1 if it does not.
|
|
500
|
+
- If `options.definitions` is provided, verifies that file exists; logs an error and exits with status code 1 if it does not.
|
|
501
|
+
|
|
502
|
+
Parameters:
|
|
503
|
+
options: An object with at least the attributes:
|
|
504
|
+
- path (str): Path to the input source directory or file.
|
|
505
|
+
- definitions (Optional[str]): Path to the type definitions file (may be None or empty).
|
|
506
|
+
"""
|
|
326
507
|
path = options.path
|
|
327
508
|
|
|
328
509
|
if not os.path.exists(path):
|
|
@@ -713,21 +894,16 @@ def add_config_scope(properties):
|
|
|
713
894
|
|
|
714
895
|
def resolve_type_and_default(properties, definitions):
|
|
715
896
|
"""
|
|
716
|
-
|
|
897
|
+
Normalize property types and expand C++-style default values into JSON-compatible Python structures.
|
|
717
898
|
|
|
718
|
-
This function
|
|
719
|
-
- Resolves type references found in `properties` against `definitions` (supports "$ref" and direct type names) and normalizes property "type" to a JSON Schema primitive ("object", "string", "integer", "boolean", "array", "number") with sensible fallbacks.
|
|
720
|
-
- Expands C++ constructor/initializer syntax and common C++ patterns appearing in default values into JSON-compatible Python values (e.g., nested constructor calls -> dicts, initializer lists -> lists, `std::nullopt` -> None, enum-like tokens -> strings).
|
|
721
|
-
- Ensures array-typed properties (including one_or_many_property cases) have array defaults: single-object defaults are wrapped into a one-element list and "{}" string defaults become [].
|
|
722
|
-
- Updates array item type information when item types reference definitions.
|
|
723
|
-
- Applies a final pass to convert any remaining C++-patterned defaults and to transform any `enterprise_value` strings via process_enterprise_value.
|
|
899
|
+
This function resolves type references in each property against the provided definitions (supports "$ref" and direct type names), normalizes property "type" to a JSON Schema primitive when possible, expands C++ constructor/initializer and common C++ literal patterns found in "default" values into Python primitives/objects/lists, ensures array-typed properties have array defaults (including handling one_or_many_property cases), updates array item type information when item types reference definitions, and converts any `enterprise_value` strings via process_enterprise_value.
|
|
724
900
|
|
|
725
901
|
Parameters:
|
|
726
|
-
properties (dict): Mapping of property names to
|
|
727
|
-
definitions (dict): Mapping of
|
|
902
|
+
properties (dict): Mapping of property names to metadata dictionaries. Relevant keys that may be modified include "type", "default", "items", and "enterprise_value".
|
|
903
|
+
definitions (dict): Mapping of definition names to JSON Schema definition dictionaries used to resolve $ref targets and to infer shapes for expanding constructor-style defaults.
|
|
728
904
|
|
|
729
905
|
Returns:
|
|
730
|
-
dict: The same `properties` mapping after in-place normalization and expansion of types and
|
|
906
|
+
dict: The same `properties` mapping after in-place normalization and expansion of types, defaults, item types, and enterprise values.
|
|
731
907
|
"""
|
|
732
908
|
import ast
|
|
733
909
|
import re
|
|
@@ -742,20 +918,42 @@ def resolve_type_and_default(properties, definitions):
|
|
|
742
918
|
return defn
|
|
743
919
|
|
|
744
920
|
def parse_constructor(s):
|
|
745
|
-
"""
|
|
921
|
+
"""
|
|
922
|
+
Parse a C++-style constructor or initializer expression into its type name and argument list.
|
|
923
|
+
|
|
924
|
+
Parses input forms such as `Type(arg1, arg2)`, `Type{arg1, arg2}`, or plain literals/enum-like tokens. For string literals the returned argument is a Python string value; for integer literals the returned argument is an int. Nested constructors and nested brace/paren groups are preserved as argument tokens.
|
|
925
|
+
|
|
926
|
+
Parameters:
|
|
927
|
+
s (str): The C++ expression to parse.
|
|
928
|
+
|
|
929
|
+
Returns:
|
|
930
|
+
tuple:
|
|
931
|
+
- type_name (str|None): The parsed type name for constructor forms, or `None` when `s` is a primitive literal or enum-like token.
|
|
932
|
+
- args (list): A list of argument tokens; tokens are raw strings for complex/nested arguments, Python `str` for quoted string literals, or `int` for integer literals.
|
|
933
|
+
"""
|
|
746
934
|
s = s.strip()
|
|
935
|
+
original_s = s
|
|
747
936
|
if s.startswith("{") and s.endswith("}"):
|
|
748
937
|
s = s[1:-1].strip()
|
|
938
|
+
|
|
939
|
+
# Try parentheses syntax first: type_name(args)
|
|
749
940
|
match = re.match(r'([a-zA-Z0-9_:]+)\((.*)\)', s)
|
|
750
|
-
if
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
941
|
+
if match:
|
|
942
|
+
type_name, arg_str = match.groups()
|
|
943
|
+
else:
|
|
944
|
+
# Try curly brace syntax: type_name{args}
|
|
945
|
+
match = re.match(r'([a-zA-Z0-9_:]+)\{(.*)\}', s)
|
|
946
|
+
if match:
|
|
947
|
+
type_name, arg_str = match.groups()
|
|
948
|
+
else:
|
|
949
|
+
# Primitive or enum
|
|
950
|
+
if s.startswith('"') and s.endswith('"'):
|
|
951
|
+
return None, [ast.literal_eval(s)]
|
|
952
|
+
try:
|
|
953
|
+
return None, [int(s)]
|
|
954
|
+
except ValueError:
|
|
955
|
+
return None, [s]
|
|
956
|
+
|
|
759
957
|
args = []
|
|
760
958
|
depth = 0
|
|
761
959
|
current = ''
|
|
@@ -768,9 +966,9 @@ def resolve_type_and_default(properties, definitions):
|
|
|
768
966
|
args.append(current.strip())
|
|
769
967
|
current = ''
|
|
770
968
|
else:
|
|
771
|
-
if c
|
|
969
|
+
if c in '({' and not in_string:
|
|
772
970
|
depth += 1
|
|
773
|
-
elif c
|
|
971
|
+
elif c in ')}' and not in_string:
|
|
774
972
|
depth -= 1
|
|
775
973
|
current += c
|
|
776
974
|
if current.strip():
|
|
@@ -779,13 +977,18 @@ def resolve_type_and_default(properties, definitions):
|
|
|
779
977
|
|
|
780
978
|
def process_cpp_patterns(arg_str):
|
|
781
979
|
"""
|
|
782
|
-
|
|
980
|
+
Convert a C++-style expression string into a JSON-friendly literal representation.
|
|
783
981
|
|
|
784
|
-
|
|
785
|
-
- net::unresolved_address("127.0.0.1", 9092) -> expands based on type definition
|
|
982
|
+
This function recognises common C++ patterns produced by the extractor and maps them to values suitable for JSON schema defaults and examples. Handled cases include:
|
|
786
983
|
- std::nullopt -> null
|
|
787
|
-
-
|
|
788
|
-
-
|
|
984
|
+
- zero-argument functions (e.g., model::kafka_audit_logging_topic()) resolved from source when possible
|
|
985
|
+
- enum tokens (e.g., fips_mode_flag::disabled -> "disabled")
|
|
986
|
+
- constexpr identifiers and simple string constructors resolved to their literal strings when available
|
|
987
|
+
- known default constructors and truncated type names mapped to sensible defaults (e.g., duration -> 0, path -> "")
|
|
988
|
+
- simple heuristics for unknown constructors and concatenated expressions
|
|
989
|
+
|
|
990
|
+
Returns:
|
|
991
|
+
processed (str): A string representing the JSON-ready value (for example: '"value"', 'null', '0', or the original input when no mapping applied).
|
|
789
992
|
"""
|
|
790
993
|
arg_str = arg_str.strip()
|
|
791
994
|
|
|
@@ -808,6 +1011,24 @@ def resolve_type_and_default(properties, definitions):
|
|
|
808
1011
|
enum_value = enum_match.group(1)
|
|
809
1012
|
return f'"{enum_value}"'
|
|
810
1013
|
|
|
1014
|
+
# Handle constexpr identifier resolution (such as scram -> "SCRAM")
|
|
1015
|
+
# Check if this is a simple identifier that might be a constexpr variable
|
|
1016
|
+
if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', arg_str):
|
|
1017
|
+
resolved_value = resolve_constexpr_identifier(arg_str)
|
|
1018
|
+
if resolved_value is not None:
|
|
1019
|
+
return f'"{resolved_value}"'
|
|
1020
|
+
|
|
1021
|
+
# Handle string constructor patterns like ss::sstring{identifier}
|
|
1022
|
+
sstring_match = re.match(r'ss::sstring\{([a-zA-Z_][a-zA-Z0-9_]*)\}', arg_str)
|
|
1023
|
+
if sstring_match:
|
|
1024
|
+
identifier = sstring_match.group(1)
|
|
1025
|
+
resolved_value = resolve_constexpr_identifier(identifier)
|
|
1026
|
+
if resolved_value is not None:
|
|
1027
|
+
return f'"{resolved_value}"'
|
|
1028
|
+
else:
|
|
1029
|
+
# Fallback to the identifier itself
|
|
1030
|
+
return f'"{identifier}"'
|
|
1031
|
+
|
|
811
1032
|
# Handle default constructors and their default values
|
|
812
1033
|
# This handles cases where C++ default constructors are used but should map to specific values
|
|
813
1034
|
|
|
@@ -861,12 +1082,23 @@ def resolve_type_and_default(properties, definitions):
|
|
|
861
1082
|
|
|
862
1083
|
def expand_default(type_name, default_str):
|
|
863
1084
|
"""
|
|
864
|
-
|
|
1085
|
+
Convert a C++-style default initializer into a JSON-serializable Python value.
|
|
865
1086
|
|
|
866
|
-
|
|
867
|
-
|
|
1087
|
+
This expands C++ constructor and initializer-list syntax into Python primitives, dictionaries, and lists suitable for JSON output. Supported transformations include:
|
|
1088
|
+
- String constructors and quoted literals → Python str.
|
|
1089
|
+
- Integer and boolean literals → Python int and bool.
|
|
1090
|
+
- Object constructors (Type(arg1, arg2) or Type{...}) → dict mapping constructor arguments to the object's properties when a corresponding type definition exists.
|
|
1091
|
+
- Nested constructors → nested dicts with their fields expanded.
|
|
1092
|
+
- Array initializer lists (e.g., {Type(...), Type(...)}) → Python list with each element expanded.
|
|
1093
|
+
- Special-case mappings for known type patterns (for example, an address-type constructor expanded into {"address", "port"} when the target type expects that shape).
|
|
1094
|
+
If a default cannot be resolved or the type is an enum, the original input is returned unchanged; the string "null" is converted to None. If default_str is not a string, it is returned as-is.
|
|
868
1095
|
|
|
869
|
-
|
|
1096
|
+
Parameters:
|
|
1097
|
+
type_name (str): The resolved type name for the default value (e.g., "model::broker_endpoint" or a primitive type like "string").
|
|
1098
|
+
default_str (str | any): The C++ default expression to expand, or a non-string value already decoded.
|
|
1099
|
+
|
|
1100
|
+
Returns:
|
|
1101
|
+
The expanded Python representation of the default: a dict for objects, a list for arrays, a primitive (str/int/bool), None for null, or the original value/string when expansion is not possible.
|
|
870
1102
|
"""
|
|
871
1103
|
# Handle non-string defaults
|
|
872
1104
|
if not isinstance(default_str, str):
|
|
@@ -883,6 +1115,19 @@ def resolve_type_and_default(properties, definitions):
|
|
|
883
1115
|
return ast.literal_eval(processed)
|
|
884
1116
|
else:
|
|
885
1117
|
return processed
|
|
1118
|
+
|
|
1119
|
+
# Handle string type with constructor syntax (e.g., ss::sstring{scram})
|
|
1120
|
+
if type_name == "string" and ("{" in default_str or "(" in default_str):
|
|
1121
|
+
tname, args = parse_constructor(default_str)
|
|
1122
|
+
if tname and args:
|
|
1123
|
+
# For string constructors, resolve the first argument and return it as the string value
|
|
1124
|
+
first_arg = args[0] if args else ""
|
|
1125
|
+
# Apply C++ pattern processing to resolve identifiers
|
|
1126
|
+
processed_arg = process_cpp_patterns(first_arg)
|
|
1127
|
+
if processed_arg.startswith('"') and processed_arg.endswith('"'):
|
|
1128
|
+
return ast.literal_eval(processed_arg) # Remove quotes
|
|
1129
|
+
else:
|
|
1130
|
+
return processed_arg
|
|
886
1131
|
|
|
887
1132
|
type_def = resolve_definition_type(definitions.get(type_name, {}))
|
|
888
1133
|
if "enum" in type_def:
|
|
@@ -1619,4 +1864,4 @@ def main():
|
|
|
1619
1864
|
sys.exit(1)
|
|
1620
1865
|
|
|
1621
1866
|
if __name__ == "__main__":
|
|
1622
|
-
main()
|
|
1867
|
+
main()
|
|
@@ -12,12 +12,12 @@ logger = logging.getLogger(__name__)
|
|
|
12
12
|
# the centralized enterprise value processing logic without creating import cycles.
|
|
13
13
|
def get_process_enterprise_value():
|
|
14
14
|
"""
|
|
15
|
-
Lazily
|
|
15
|
+
Lazily load the centralized process_enterprise_value function from property_extractor.
|
|
16
16
|
|
|
17
|
-
Attempts to import
|
|
17
|
+
Attempts to import and return the `process_enterprise_value` callable; logs an error and returns `None` if the import fails.
|
|
18
18
|
|
|
19
19
|
Returns:
|
|
20
|
-
|
|
20
|
+
The `process_enterprise_value` callable if available, `None` otherwise.
|
|
21
21
|
"""
|
|
22
22
|
try:
|
|
23
23
|
from property_extractor import process_enterprise_value
|
|
@@ -27,6 +27,23 @@ def get_process_enterprise_value():
|
|
|
27
27
|
return None
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
def get_resolve_constexpr_identifier():
|
|
31
|
+
"""
|
|
32
|
+
Lazily import and return the `resolve_constexpr_identifier` function from `property_extractor`.
|
|
33
|
+
|
|
34
|
+
Attempts to import `resolve_constexpr_identifier` and return it to avoid circular-import issues.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Callable or None: The `resolve_constexpr_identifier` callable when available, otherwise `None`.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
from property_extractor import resolve_constexpr_identifier
|
|
41
|
+
return resolve_constexpr_identifier
|
|
42
|
+
except ImportError as e:
|
|
43
|
+
logger.exception("Cannot import resolve_constexpr_identifier from property_extractor: %s", e)
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
30
47
|
class BasicInfoTransformer:
|
|
31
48
|
def accepts(self, info, file_pair):
|
|
32
49
|
"""
|
|
@@ -182,29 +199,59 @@ class TypeTransformer:
|
|
|
182
199
|
|
|
183
200
|
def get_cpp_type_from_declaration(self, declaration):
|
|
184
201
|
"""
|
|
185
|
-
Extract the inner type from
|
|
186
|
-
|
|
187
|
-
This method handles various C++ template types and extracts the core type T from:
|
|
188
|
-
- property<T> -> T
|
|
189
|
-
- std::optional<T> -> T
|
|
190
|
-
- std::vector<T> -> T
|
|
191
|
-
- one_or_many_property<T> -> T (Redpanda's flexible array type)
|
|
202
|
+
Extract the inner C++ type from wrapped declarations like `property<T>`, `std::optional<T>`, `std::vector<T>`, or `one_or_many_property<T>`.
|
|
192
203
|
|
|
193
|
-
|
|
194
|
-
to accept either a single value or an array of values in the configuration.
|
|
195
|
-
Examples:
|
|
196
|
-
- one_or_many_property<model::broker_endpoint> -> model::broker_endpoint
|
|
197
|
-
- one_or_many_property<endpoint_tls_config> -> endpoint_tls_config
|
|
204
|
+
Parses common wrapper templates and returns the unwrapped type name (for example, returns `model::broker_endpoint` from `one_or_many_property<model::broker_endpoint>`). The returned type is intended for downstream mapping to JSON schema types and default value resolution.
|
|
198
205
|
|
|
199
|
-
|
|
200
|
-
|
|
206
|
+
Returns:
|
|
207
|
+
raw_type (str): The extracted inner C++ type as a string, or a best-effort fragment of the declaration if a precise extraction cannot be performed.
|
|
201
208
|
"""
|
|
202
209
|
one_line_declaration = declaration.replace("\n", "").strip()
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
210
|
+
|
|
211
|
+
# Extract property template content with proper nesting handling
|
|
212
|
+
# This handles cases like property<std::vector<config::sasl_mechanisms_override>>
|
|
213
|
+
def extract_template_content(text, template_name):
|
|
214
|
+
"""
|
|
215
|
+
Extracts the inner contents of the first occurrence of a template with the given name, correctly handling nested angle brackets.
|
|
216
|
+
|
|
217
|
+
Parameters:
|
|
218
|
+
text (str): The string to search for the template.
|
|
219
|
+
template_name (str): The template name (e.g., "std::vector" or "property").
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
str or None: The substring inside the outermost angle brackets for the matched template (excluding the brackets),
|
|
223
|
+
or `None` if the template is not found or angle brackets are unbalanced.
|
|
224
|
+
"""
|
|
225
|
+
start_idx = text.find(f'{template_name}<')
|
|
226
|
+
if start_idx == -1:
|
|
227
|
+
return None
|
|
228
|
+
|
|
229
|
+
start_idx += len(f'{template_name}<')
|
|
230
|
+
bracket_count = 1
|
|
231
|
+
i = start_idx
|
|
232
|
+
|
|
233
|
+
while i < len(text) and bracket_count > 0:
|
|
234
|
+
if text[i] == '<':
|
|
235
|
+
bracket_count += 1
|
|
236
|
+
elif text[i] == '>':
|
|
237
|
+
bracket_count -= 1
|
|
238
|
+
i += 1
|
|
239
|
+
|
|
240
|
+
if bracket_count == 0:
|
|
241
|
+
return text[start_idx:i-1]
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
# Extract the content from property<...>
|
|
245
|
+
property_content = extract_template_content(one_line_declaration, 'property')
|
|
246
|
+
if property_content:
|
|
247
|
+
raw_type = property_content.split()[0].replace(",", "")
|
|
248
|
+
else:
|
|
249
|
+
# Fallback to original regex for simpler cases
|
|
250
|
+
raw_type = (
|
|
251
|
+
re.sub(r"^.*property<(.+)>.*", "\\1", one_line_declaration)
|
|
252
|
+
.split()[0]
|
|
253
|
+
.replace(",", "")
|
|
254
|
+
)
|
|
208
255
|
|
|
209
256
|
if self.OPTIONAL_PATTERN in raw_type:
|
|
210
257
|
raw_type = re.sub(".*std::optional<(.+)>.*", "\\1", raw_type)
|
|
@@ -223,6 +270,15 @@ class TypeTransformer:
|
|
|
223
270
|
return raw_type
|
|
224
271
|
|
|
225
272
|
def get_type_from_declaration(self, declaration):
|
|
273
|
+
"""
|
|
274
|
+
Map a C++ type declaration string to a simplified, user-facing type name.
|
|
275
|
+
|
|
276
|
+
Parameters:
|
|
277
|
+
declaration (str): C++ type declaration or template expression from which the effective type will be derived.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
str: A JSON-schema-friendly type name such as "integer", "number", "string", "string[]", or "boolean". If no mapping matches, returns the normalized/raw extracted C++ type.
|
|
281
|
+
"""
|
|
226
282
|
raw_type = self.get_cpp_type_from_declaration(declaration)
|
|
227
283
|
type_mapping = [ # (regex, type)
|
|
228
284
|
("^u(nsigned|int)", "integer"),
|
|
@@ -240,9 +296,29 @@ class TypeTransformer:
|
|
|
240
296
|
if re.search(m[0], raw_type):
|
|
241
297
|
return m[1]
|
|
242
298
|
|
|
299
|
+
# Handle specific user-unfriendly C++ types with descriptive alternatives
|
|
300
|
+
# Map complex C++ config types to user-friendly JSON schema types
|
|
301
|
+
user_friendly_types = {
|
|
302
|
+
"config::sasl_mechanisms_override": "object",
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if raw_type in user_friendly_types:
|
|
306
|
+
return user_friendly_types[raw_type]
|
|
307
|
+
|
|
243
308
|
return raw_type
|
|
244
309
|
|
|
245
310
|
def parse(self, property, info, file_pair):
|
|
311
|
+
"""
|
|
312
|
+
Set the property's "type" field to the JSON schema type derived from the C++ declaration.
|
|
313
|
+
|
|
314
|
+
Parameters:
|
|
315
|
+
property (dict): Mutable property bag to be updated.
|
|
316
|
+
info (dict): Parsed property metadata; its "declaration" field is used to determine the type.
|
|
317
|
+
file_pair: Unused here; present for transformer interface compatibility.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
property (dict): The same property bag with "type" set to the derived type string.
|
|
321
|
+
"""
|
|
246
322
|
property["type"] = self.get_type_from_declaration(info["declaration"])
|
|
247
323
|
return property
|
|
248
324
|
|
|
@@ -394,42 +470,170 @@ class FriendlyDefaultTransformer:
|
|
|
394
470
|
|
|
395
471
|
# Class-level constants for pattern matching in default values
|
|
396
472
|
ARRAY_PATTERN_STD_VECTOR = "std::vector"
|
|
473
|
+
SSTRING_CONSTRUCTOR_PATTERN = r'ss::sstring\{([a-zA-Z_][a-zA-Z0-9_]*)\}'
|
|
474
|
+
VECTOR_INITIALIZER_PATTERN = r'std::vector<[^>]+>\s*\{(.*)\}$'
|
|
475
|
+
CHRONO_PATTERN = r"std::chrono::(\w+)\(([^)]+)\)"
|
|
476
|
+
|
|
477
|
+
def __init__(self):
|
|
478
|
+
"""
|
|
479
|
+
Initialize the transformer and set up a placeholder for a lazily-loaded resolver.
|
|
480
|
+
|
|
481
|
+
Sets self._resolver to None to indicate the resolver has not been loaded yet.
|
|
482
|
+
"""
|
|
483
|
+
self._resolver = None
|
|
397
484
|
|
|
398
485
|
def accepts(self, info, file_pair):
|
|
486
|
+
"""
|
|
487
|
+
Determine whether the transformer should run for the given property info by checking for a fourth parameter.
|
|
488
|
+
|
|
489
|
+
Parameters:
|
|
490
|
+
info (dict): Parsed property metadata; expects a "params" list when present.
|
|
491
|
+
file_pair (tuple): Source/implementation file pair (unused by this check).
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
`true` if `info["params"]` exists and contains at least four items, `false` otherwise.
|
|
495
|
+
"""
|
|
399
496
|
return info.get("params") and len(info["params"]) > 3
|
|
400
497
|
|
|
498
|
+
def _get_resolver(self):
|
|
499
|
+
"""
|
|
500
|
+
Lazily load and cache the constexpr identifier resolver.
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
callable or None: The resolver function if available, or `None` if it could not be loaded.
|
|
504
|
+
"""
|
|
505
|
+
if self._resolver is None:
|
|
506
|
+
resolver = get_resolve_constexpr_identifier()
|
|
507
|
+
self._resolver = resolver if resolver else False
|
|
508
|
+
return self._resolver if self._resolver is not False else None
|
|
509
|
+
|
|
510
|
+
def _resolve_identifier(self, identifier):
|
|
511
|
+
"""
|
|
512
|
+
Resolve a constexpr identifier to its corresponding string value.
|
|
513
|
+
|
|
514
|
+
Parameters:
|
|
515
|
+
identifier (str): Identifier to resolve (for example, "scram" or "gssapi").
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
str or None: The resolved string value if successful, `None` when the identifier is invalid or cannot be resolved.
|
|
519
|
+
"""
|
|
520
|
+
if not identifier or not isinstance(identifier, str):
|
|
521
|
+
logger.warning(f"Invalid identifier for resolution: {identifier}")
|
|
522
|
+
return None
|
|
523
|
+
|
|
524
|
+
resolver = self._get_resolver()
|
|
525
|
+
if resolver:
|
|
526
|
+
try:
|
|
527
|
+
return resolver(identifier)
|
|
528
|
+
except (AttributeError, TypeError, ValueError) as e:
|
|
529
|
+
logger.debug(f"Failed to resolve identifier '{identifier}': {e}")
|
|
530
|
+
except Exception as e:
|
|
531
|
+
logger.exception(f"Unexpected error resolving identifier '{identifier}': {e}")
|
|
532
|
+
|
|
533
|
+
return None
|
|
534
|
+
|
|
535
|
+
def _process_sstring_constructor(self, item):
|
|
536
|
+
"""
|
|
537
|
+
Convert an ss::sstring{identifier} constructor string to its resolved value when possible.
|
|
538
|
+
|
|
539
|
+
If the input matches the ss::sstring{...} pattern, attempts to resolve the enclosed identifier and returns the resolved string. If resolution fails, returns the raw identifier. If the input does not match the pattern or is falsy, returns it unchanged.
|
|
540
|
+
|
|
541
|
+
Parameters:
|
|
542
|
+
item (str): The constructor expression or string to process.
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
str: The resolved string when resolution succeeds, the extracted identifier if resolution fails, or the original input if it does not match.
|
|
546
|
+
"""
|
|
547
|
+
if not item:
|
|
548
|
+
return item
|
|
549
|
+
|
|
550
|
+
match = re.match(self.SSTRING_CONSTRUCTOR_PATTERN, item)
|
|
551
|
+
if not match:
|
|
552
|
+
return item
|
|
553
|
+
|
|
554
|
+
identifier = match.group(1)
|
|
555
|
+
resolved = self._resolve_identifier(identifier)
|
|
556
|
+
|
|
557
|
+
if resolved:
|
|
558
|
+
logger.debug(f"Resolved ss::sstring{{{identifier}}} -> '{resolved}'")
|
|
559
|
+
return resolved
|
|
560
|
+
|
|
561
|
+
# Log warning but continue with original identifier
|
|
562
|
+
logger.warning(f"Could not resolve identifier '{identifier}' in ss::sstring constructor")
|
|
563
|
+
return identifier
|
|
564
|
+
|
|
565
|
+
def _parse_vector_contents(self, contents):
|
|
566
|
+
"""
|
|
567
|
+
Parse a comma-separated std::vector initializer string into a list of cleaned, processed items.
|
|
568
|
+
|
|
569
|
+
Parameters:
|
|
570
|
+
contents (str): The inner contents of a vector initializer (e.g. '\"a\", ss::sstring{ID}, \"b\"'); may be empty or None.
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
list: Ordered list of processed, unquoted items with empty entries omitted.
|
|
574
|
+
"""
|
|
575
|
+
if not contents:
|
|
576
|
+
return []
|
|
577
|
+
|
|
578
|
+
# Split by comma and process each item
|
|
579
|
+
raw_items = [contents] if ',' not in contents else contents.split(',')
|
|
580
|
+
|
|
581
|
+
processed_items = []
|
|
582
|
+
for item in raw_items:
|
|
583
|
+
item = item.strip(' "\'')
|
|
584
|
+
if item: # Skip empty items
|
|
585
|
+
processed_item = self._process_sstring_constructor(item)
|
|
586
|
+
processed_items.append(processed_item)
|
|
587
|
+
|
|
588
|
+
return processed_items
|
|
589
|
+
|
|
401
590
|
def parse(self, property, info, file_pair):
|
|
591
|
+
"""
|
|
592
|
+
Convert a C++ default expression into a JSON-friendly value and store it on the property under the "default" key.
|
|
593
|
+
|
|
594
|
+
Parameters:
|
|
595
|
+
property (dict): Property dictionary to modify; updated in place with a "default" entry.
|
|
596
|
+
info (dict): Parsed property information; the default expression is expected at info["params"][3]["value"].
|
|
597
|
+
file_pair: File pair context (ignored by this function).
|
|
598
|
+
|
|
599
|
+
Returns:
|
|
600
|
+
dict: The modified property dictionary with a normalized "default" value.
|
|
601
|
+
"""
|
|
402
602
|
default = info["params"][3]["value"]
|
|
603
|
+
|
|
604
|
+
# Handle null/empty defaults
|
|
605
|
+
if not default:
|
|
606
|
+
return property
|
|
403
607
|
|
|
404
|
-
# Transform std::nullopt into None
|
|
608
|
+
# Transform std::nullopt into None
|
|
405
609
|
if "std::nullopt" in default:
|
|
406
610
|
property["default"] = None
|
|
407
611
|
return property
|
|
408
612
|
|
|
409
|
-
# Transform std::numeric_limits expressions
|
|
613
|
+
# Transform std::numeric_limits expressions
|
|
410
614
|
if "std::numeric_limits" in default:
|
|
411
615
|
property["default"] = "Maximum value"
|
|
412
616
|
return property
|
|
413
617
|
|
|
414
|
-
# Transform std::chrono durations
|
|
618
|
+
# Transform std::chrono durations
|
|
415
619
|
if "std::chrono" in default:
|
|
416
|
-
|
|
417
|
-
if
|
|
418
|
-
unit =
|
|
419
|
-
value =
|
|
620
|
+
match = re.search(self.CHRONO_PATTERN, default)
|
|
621
|
+
if match:
|
|
622
|
+
unit = match.group(1)
|
|
623
|
+
value = match.group(2).strip()
|
|
420
624
|
property["default"] = f"{value} {unit}"
|
|
421
625
|
return property
|
|
422
626
|
|
|
423
|
-
# Transform std::vector defaults
|
|
627
|
+
# Transform std::vector defaults
|
|
424
628
|
if self.ARRAY_PATTERN_STD_VECTOR in default:
|
|
425
|
-
|
|
426
|
-
if
|
|
427
|
-
contents =
|
|
428
|
-
items =
|
|
629
|
+
vector_match = re.search(self.VECTOR_INITIALIZER_PATTERN, default)
|
|
630
|
+
if vector_match:
|
|
631
|
+
contents = vector_match.group(1).strip()
|
|
632
|
+
items = self._parse_vector_contents(contents)
|
|
429
633
|
property["default"] = items
|
|
430
634
|
return property
|
|
431
635
|
|
|
432
|
-
#
|
|
636
|
+
# For all other cases, leave the default as-is
|
|
433
637
|
property["default"] = default
|
|
434
638
|
return property
|
|
435
639
|
|
|
@@ -566,4 +770,4 @@ class MetaParamTransformer:
|
|
|
566
770
|
key, value = item.split('=')
|
|
567
771
|
meta_dict[key.strip().replace('.', '')] = value.strip()
|
|
568
772
|
meta_dict['type'] = 'initializer_list' # Enforce required type
|
|
569
|
-
param['value'] = meta_dict
|
|
773
|
+
param['value'] = meta_dict
|