Skip to content

Commit f2ea717

Browse files
committed
fix: Handle nested tables in TOML encoder
1 parent a2f910f commit f2ea717

1 file changed

Lines changed: 69 additions & 11 deletions

File tree

src/Encoder/ArrayToDocumentConverter.php

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ public function convert(array $data): Document
2626
$nodes = [];
2727
$position = new Position(1, 1, 0);
2828

29-
// Categorize data into root entries, tables, and table arrays
30-
[$rootEntries, $tables, $tableArrays] = $this->categorizeData($data);
29+
// Flatten the data structure to handle nested tables
30+
$flatData = $this->flattenData($data);
31+
32+
// Categorize flattened data into root entries, tables, and table arrays
33+
[$rootEntries, $tables, $tableArrays] = $this->categorizeData($flatData);
3134

3235
// Root key-value pairs first
3336
foreach ($rootEntries as $key => $value) {
@@ -49,6 +52,65 @@ public function convert(array $data): Document
4952
return new Document($nodes, $position);
5053
}
5154

55+
/**
56+
* Flattens nested array structure into dotted keys.
57+
*
58+
* Example:
59+
* ['github' => ['token' => ['key' => 'val']]]
60+
* becomes:
61+
* ['github.token' => ['key' => 'val']]
62+
*
63+
* @param array<string, mixed> $data
64+
* @param string $prefix
65+
* @return array<string, mixed>
66+
*/
67+
private function flattenData(array $data, string $prefix = ''): array
68+
{
69+
$result = [];
70+
71+
foreach ($data as $key => $value) {
72+
if (!\is_string($key)) {
73+
throw new \InvalidArgumentException('TOML keys must be strings, got: ' . \get_debug_type($key));
74+
}
75+
76+
$fullKey = $prefix === '' ? $key : $prefix . '.' . $key;
77+
78+
if (!\is_array($value)) {
79+
// Scalar value
80+
$result[$fullKey] = $value;
81+
} elseif ($this->isTableArray($value)) {
82+
// Table array - keep as is
83+
$result[$fullKey] = $value;
84+
} elseif ($this->isAssociativeArray($value)) {
85+
// Check if this table has only scalar/array values (leaf table)
86+
// or if it has nested tables
87+
$hasNestedTables = false;
88+
foreach ($value as $subValue) {
89+
if (\is_array($subValue) && $this->isAssociativeArray($subValue) && !$this->isTableArray($subValue)) {
90+
$hasNestedTables = true;
91+
break;
92+
}
93+
}
94+
95+
if ($hasNestedTables) {
96+
// Recursively flatten nested tables
97+
$flattened = $this->flattenData($value, $fullKey);
98+
foreach ($flattened as $flatKey => $flatValue) {
99+
$result[$flatKey] = $flatValue;
100+
}
101+
} else {
102+
// Leaf table - keep as is
103+
$result[$fullKey] = $value;
104+
}
105+
} else {
106+
// Simple array (list of scalars)
107+
$result[$fullKey] = $value;
108+
}
109+
}
110+
111+
return $result;
112+
}
113+
52114
/**
53115
* Categorizes data into root entries, tables, and table arrays.
54116
*
@@ -102,18 +164,14 @@ private function createTable(string $name, array $data): Table
102164
$entries = [];
103165
$position = new Position(0, 0, 0);
104166

105-
// Categorize table data
106-
[$rootEntries, $nestedTables, $nestedTableArrays] = $this->categorizeData($data);
107-
108-
// Add root entries
109-
foreach ($rootEntries as $key => $value) {
167+
// Add all entries (data is already flattened, so no nested tables here)
168+
foreach ($data as $key => $value) {
169+
if (!\is_string($key)) {
170+
throw new \InvalidArgumentException('TOML keys must be strings, got: ' . \get_debug_type($key));
171+
}
110172
$entries[] = $this->createEntry($key, $value);
111173
}
112174

113-
// Nested tables and table arrays will be handled at document level
114-
// For now, we only handle simple table entries
115-
// TODO: Handle nested structures properly
116-
117175
return new Table($keyNode, $entries, null, $position);
118176
}
119177

0 commit comments

Comments
 (0)