Skip to content

Commit f72ceb9

Browse files
committed
feat: add Children and ChildNodes methods
1 parent 15733e6 commit f72ceb9

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

node.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,42 @@ func (n *Node) InnerText() string {
142142
return b.String()
143143
}
144144

145+
// IsElementLike returns true if the node type supports child nodes.
146+
func (n *Node) IsElementLike() bool {
147+
switch n.Type {
148+
case ElementNode, DocumentNode, DeclarationNode:
149+
return true
150+
default:
151+
return false
152+
}
153+
}
154+
155+
// Children returns a slice of all direct child nodes of the current node.
156+
// This includes all node types that are children, without filtering.
157+
func (n *Node) Children() []*Node {
158+
var children []*Node
159+
for child := n.FirstChild; child != nil; child = child.NextSibling {
160+
children = append(children, child)
161+
}
162+
return children
163+
}
164+
165+
// ChildNodes returns a slice of all direct child nodes of the current node (excluding attributes, text, comments, and char data).
166+
// Returns an error if the node type does not support child nodes.
167+
func (n *Node) ChildNodes() ([]*Node, error) {
168+
if !n.IsElementLike() {
169+
return nil, fmt.Errorf("node type %v does not support child nodes", n.Type)
170+
}
171+
172+
var children []*Node
173+
for child := n.FirstChild; child != nil; child = child.NextSibling {
174+
if child.IsElementLike() {
175+
children = append(children, child)
176+
}
177+
}
178+
return children, nil
179+
}
180+
145181
func (n *Node) sanitizedData(preserveSpaces bool) string {
146182
if preserveSpaces {
147183
return n.Data

node_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,44 @@ func verifyNodePointers(t *testing.T, n *Node) {
117117
testTrue(t, parent == nil || parent.LastChild == cur)
118118
}
119119

120+
func TestChildren(t *testing.T) {
121+
t.Run("Has 3 children", func(t *testing.T) {
122+
node := &Node{Type: ElementNode}
123+
124+
AddChild(node, &Node{Type: CommentNode})
125+
AddChild(node, &Node{Type: ElementNode})
126+
AddChild(node, &Node{Type: TextNode})
127+
128+
children := node.Children()
129+
130+
testTrue(t, len(children) == 3)
131+
})
132+
}
133+
134+
func TestChildNodes(t *testing.T) {
135+
t.Run("Has 1 child node", func(t *testing.T) {
136+
node := &Node{Type: ElementNode}
137+
138+
AddChild(node, &Node{Type: CommentNode})
139+
AddChild(node, &Node{Type: ElementNode})
140+
AddChild(node, &Node{Type: TextNode})
141+
142+
children, err := node.ChildNodes()
143+
144+
testTrue(t, err == nil)
145+
testTrue(t, len(children) == 1)
146+
})
147+
148+
t.Run("Cannot have child nodes", func(t *testing.T) {
149+
node := &Node{Type: CommentNode}
150+
151+
children, err := node.ChildNodes()
152+
153+
testTrue(t, children == nil)
154+
testTrue(t, err != nil)
155+
})
156+
}
157+
120158
func TestAddAttr(t *testing.T) {
121159
for _, test := range []struct {
122160
name string
@@ -232,6 +270,59 @@ func TestRemoveAttr(t *testing.T) {
232270
}
233271
}
234272

273+
func TestIsElementLike(t *testing.T) {
274+
for _, test := range []struct {
275+
name string
276+
n *Node
277+
expected bool
278+
}{
279+
{
280+
name: "DocumentNode can have children",
281+
n: &Node{Type: DocumentNode},
282+
expected: true,
283+
},
284+
{
285+
name: "DeclarationNode can have children",
286+
n: &Node{Type: DeclarationNode},
287+
expected: true,
288+
},
289+
{
290+
name: "ElementNode can have children",
291+
n: &Node{Type: ElementNode},
292+
expected: true,
293+
},
294+
{
295+
name: "TextNode cannot have children",
296+
n: &Node{Type: TextNode},
297+
expected: false,
298+
},
299+
{
300+
name: "CharDataNode cannot have children",
301+
n: &Node{Type: CharDataNode},
302+
expected: false,
303+
},
304+
{
305+
name: "CommentNode cannot have children",
306+
n: &Node{Type: CommentNode},
307+
expected: false,
308+
},
309+
{
310+
name: "AttributeNode cannot have children",
311+
n: &Node{Type: AttributeNode},
312+
expected: false,
313+
},
314+
{
315+
name: "NotationNode cannot have children",
316+
n: &Node{Type: NotationNode},
317+
expected: false,
318+
},
319+
} {
320+
t.Run(test.name, func(t *testing.T) {
321+
testValue(t, test.n.IsElementLike(), test.expected)
322+
})
323+
}
324+
}
325+
235326
func TestRemoveFromTree(t *testing.T) {
236327
xml := `<?procinst?>
237328
<!--comment-->

0 commit comments

Comments
 (0)