• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

asciimoth / pasta / 26626415405

29 May 2026 08:18AM UTC coverage: 77.855% (-0.3%) from 78.117%
26626415405

push

github

asciimoth
fix(std): fix formular menu handling by some nodes

20 of 72 new or added lines in 12 files covered. (27.78%)

1 existing line in 1 file now uncovered.

5467 of 7022 relevant lines covered (77.86%)

323.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.9
/pasta/std/node_string_split.go
1
package std
2

3
import (
4
        "strings"
5

6
        "github.com/asciimoth/configer/configer"
7
        "github.com/asciimoth/formular"
8
        "github.com/asciimoth/pasta/pasta"
9
)
10

11
// NodeTypeStringSplit is the class name for StringSplitClass.
12
const NodeTypeStringSplit = "pasta/StringSplit"
13

14
// StringSplitClass creates two-input nodes that split a string at the first
15
// separator occurrence and output the before and after parts.
16
type StringSplitClass struct{}
17

18
func (StringSplitClass) ClassName() string        { return NodeTypeStringSplit }
198✔
19
func (StringSplitClass) ShortDescription() string { return "Split string" }
179✔
20
func (StringSplitClass) LongDescription() string {
×
21
        return "Splits Text at the first Separator occurrence. Before receives the text before the separator and After receives the text after it. If the separator is empty or missing, Before is Text and After is empty."
×
22
}
×
23
func (StringSplitClass) DefaultNodeParams() pasta.NodeClassParams {
201✔
24
        return pasta.NodeClassParams{PrimaryType: TypeString, InitialPorts: []pasta.Port{
201✔
25
                {Direction: "right", Name: "Before", Types: []string{TypeString}},
201✔
26
                {Direction: "right", Name: "After", Types: []string{TypeString}},
201✔
27
                {Direction: "left", Name: "Text", Types: []string{TypeString}},
201✔
28
                {Direction: "left", Name: "Separator", Types: []string{TypeString}},
201✔
29
        }}
201✔
30
}
201✔
31
func (StringSplitClass) NewNode(configer.Config, ...*pasta.NodeClassState) (pasta.Node, error) {
1✔
32
        return newStringSplitNode(), nil
1✔
33
}
1✔
34

35
type stringSplitNode struct {
36
        pasta.BasicNode
37

38
        inputs map[uint64]string
39
        before string
40
        after  string
41

42
        w  *pasta.Workspace
43
        id uint64
44

45
        beforePort    uint64
46
        afterPort     uint64
47
        textPort      uint64
48
        separatorPort uint64
49
}
50

51
func newStringSplitNode() *stringSplitNode {
1✔
52
        return &stringSplitNode{inputs: map[uint64]string{}}
1✔
53
}
1✔
54

55
func (n *stringSplitNode) OnInit(w *pasta.Workspace, _ pasta.Logger, id uint64, _ string, restored *pasta.NodeInitData, _, _, _, _ bool) error {
1✔
56
        n.w = w
1✔
57
        n.id = id
1✔
58
        if restored != nil {
2✔
59
                for _, port := range restored.RightPorts {
3✔
60
                        snapshot, ok := w.PortSnapshot(port)
2✔
61
                        if !ok {
2✔
62
                                continue
×
63
                        }
64
                        switch snapshot.Name {
2✔
65
                        case "Before":
1✔
66
                                n.beforePort = port
1✔
67
                        case "After":
1✔
68
                                n.afterPort = port
1✔
69
                        }
70
                }
71
                for _, port := range restored.LeftPorts {
3✔
72
                        snapshot, ok := w.PortSnapshot(port)
2✔
73
                        if !ok {
2✔
74
                                continue
×
75
                        }
76
                        switch snapshot.Name {
2✔
77
                        case "Text":
1✔
78
                                n.textPort = port
1✔
79
                        case "Separator":
1✔
80
                                n.separatorPort = port
1✔
81
                        }
82
                }
83
        }
84
        if err := n.w.SetNodePrimary(n.id, TypeString); err != nil {
1✔
85
                return err
×
86
        }
×
87
        n.recalculate(false)
1✔
88
        if err := n.updateLabel(); err != nil {
1✔
89
                return err
×
90
        }
×
91
        n.sendMenuSnapshot()
1✔
92
        return nil
1✔
93
}
94

95
func (n *stringSplitNode) OnReady() error {
1✔
96
        n.requestAll()
1✔
97
        n.sendAll()
1✔
98
        return nil
1✔
99
}
1✔
100

101
func (n *stringSplitNode) PreLinkAdd(port uint64, linkType, portDirection string) error {
4✔
102
        if linkType != TypeString {
4✔
NEW
103
                return pasta.LinkTypeErr(linkType)
×
104
        }
×
105
        if portDirection == "left" {
6✔
106
                snapshot, ok := n.w.PortSnapshot(port)
2✔
107
                if ok && len(snapshot.Links) > 0 {
2✔
108
                        return pasta.ErrLinkDup
×
109
                }
×
110
                return nil
2✔
111
        }
112
        if port == n.beforePort || port == n.afterPort {
4✔
113
                return nil
2✔
114
        }
2✔
NEW
115
        return pasta.LinkTypeErr(linkType)
×
116
}
117

118
func (n *stringSplitNode) OnLinkAdd(link, port uint64, _ string, portDirection string) error {
4✔
119
        if portDirection == "left" {
6✔
120
                n.requestLink(link, port)
2✔
121
                return nil
2✔
122
        }
2✔
123
        n.sendToLink(link, port)
2✔
124
        return nil
2✔
125
}
126

127
func (n *stringSplitNode) OnLinkRemoved(_ uint64, port uint64, _ string, portDirection string) error {
×
128
        if portDirection == "left" {
×
129
                delete(n.inputs, port)
×
130
                n.recalculate(true)
×
131
        }
×
132
        return nil
×
133
}
134

135
func (n *stringSplitNode) OnEvent(event pasta.Event, linkType string, _ []string, receiverPortDirection string) error {
7✔
136
        if linkType != TypeString {
7✔
137
                return nil
×
138
        }
×
139
        if receiverPortDirection == "right" {
9✔
140
                if isValueRequest(event.Payload) {
4✔
141
                        n.w.SendEvent(pasta.Event{
2✔
142
                                SenderNode:   n.id,
2✔
143
                                SenderPort:   event.ReceiverPort,
2✔
144
                                ReceiverNode: event.SenderNode,
2✔
145
                                ReceiverPort: event.SenderPort,
2✔
146
                                Payload:      String(n.valueForPort(event.ReceiverPort)),
2✔
147
                        })
2✔
148
                }
2✔
149
                return nil
2✔
150
        }
151
        value, ok := parseStringAny(event.Payload)
5✔
152
        if !ok {
5✔
153
                return nil
×
154
        }
×
155
        n.inputs[event.ReceiverPort] = value
5✔
156
        n.recalculate(true)
5✔
157
        return nil
5✔
158
}
159

160
func (n *stringSplitNode) recalculate(broadcast bool) {
6✔
161
        oldBefore, oldAfter := n.before, n.after
6✔
162
        text := n.input(n.textPort)
6✔
163
        separator := n.input(n.separatorPort)
6✔
164
        if separator == "" {
9✔
165
                n.before = text
3✔
166
                n.after = ""
3✔
167
        } else {
6✔
168
                before, after, found := strings.Cut(text, separator)
3✔
169
                if found {
6✔
170
                        n.before = before
3✔
171
                        n.after = after
3✔
172
                } else {
3✔
173
                        n.before = text
×
174
                        n.after = ""
×
175
                }
×
176
        }
177
        _ = n.updateLabel()
6✔
178
        n.sendMenuBlock()
6✔
179
        if !broadcast {
7✔
180
                return
1✔
181
        }
1✔
182
        if oldBefore != n.before {
7✔
183
                n.sendPort(n.beforePort)
2✔
184
        }
2✔
185
        if oldAfter != n.after {
7✔
186
                n.sendPort(n.afterPort)
2✔
187
        }
2✔
188
}
189

190
func (n *stringSplitNode) input(port uint64) string {
12✔
191
        return n.inputs[port]
12✔
192
}
12✔
193

194
func (n *stringSplitNode) valueForPort(port uint64) string {
5✔
195
        if port == n.afterPort {
8✔
196
                return n.after
3✔
197
        }
3✔
198
        return n.before
2✔
199
}
200

201
func (n *stringSplitNode) requestAll() {
1✔
202
        for _, port := range []uint64{n.textPort, n.separatorPort} {
3✔
203
                snapshot, ok := n.w.PortSnapshot(port)
2✔
204
                if !ok {
2✔
205
                        continue
×
206
                }
207
                for _, link := range snapshot.Links {
2✔
208
                        n.requestLink(link, port)
×
209
                }
×
210
        }
211
}
212

213
func (n *stringSplitNode) requestLink(link, port uint64) {
2✔
214
        snapshot, ok := n.w.LinkSnapshot(link)
2✔
215
        if !ok {
2✔
216
                return
×
217
        }
×
218
        receiverNode, receiverPort := otherEndpoint(snapshot, port)
2✔
219
        n.w.SendEvent(pasta.Event{SenderNode: n.id, SenderPort: port, ReceiverNode: receiverNode, ReceiverPort: receiverPort, Payload: RequestValue{}})
2✔
220
}
221

222
func (n *stringSplitNode) sendAll() {
1✔
223
        n.sendPort(n.beforePort)
1✔
224
        n.sendPort(n.afterPort)
1✔
225
}
1✔
226

227
func (n *stringSplitNode) sendPort(port uint64) {
6✔
228
        snapshot, ok := n.w.PortSnapshot(port)
6✔
229
        if !ok {
6✔
230
                return
×
231
        }
×
232
        for _, link := range snapshot.Links {
7✔
233
                n.sendToLink(link, port)
1✔
234
        }
1✔
235
}
236

237
func (n *stringSplitNode) sendToLink(link, port uint64) {
3✔
238
        snapshot, ok := n.w.LinkSnapshot(link)
3✔
239
        if !ok {
3✔
240
                return
×
241
        }
×
242
        receiverNode, receiverPort := otherEndpoint(snapshot, port)
3✔
243
        n.w.SendEvent(pasta.Event{SenderNode: n.id, SenderPort: port, ReceiverNode: receiverNode, ReceiverPort: receiverPort, Payload: String(n.valueForPort(port))})
3✔
244
}
245

246
func (n *stringSplitNode) updateLabel() error {
7✔
247
        return n.w.SetNodeLabel(n.id, n.before+" | "+n.after)
7✔
248
}
7✔
249

250
func (n *stringSplitNode) sendMenuSnapshot() {
1✔
251
        n.w.SendNodeMenuMsg(n.id, formular.MenuSnapshotMessage{
1✔
252
                MessageBase: formular.MessageBase{Type: formular.MessageMenuSnapshot, MenuID: pasta.NodeMenuID(n.id), MenuGeneration: 1},
1✔
253
                Blocks:      []formular.Block{n.menuBlock()},
1✔
254
        })
1✔
255
}
1✔
256

257
func (n *stringSplitNode) sendMenuBlock() {
6✔
258
        if n.w == nil || n.id == 0 {
6✔
259
                return
×
260
        }
×
261
        n.w.SendNodeMenuMsg(n.id, formular.BlockSnapshotMessage{
6✔
262
                MessageBase: formular.MessageBase{Type: formular.MessageBlockSnapshot, MenuID: pasta.NodeMenuID(n.id), MenuGeneration: 1, BlockGeneration: 1},
6✔
263
                Block:       n.menuBlock(),
6✔
264
        })
6✔
265
}
266

267
func (n *stringSplitNode) menuBlock() formular.Block {
7✔
268
        return formular.Block{
7✔
269
                ID: "state", Order: 10, Generation: 1,
7✔
270
                Items: []formular.Item{
7✔
271
                        {Type: formular.ItemField, ID: "before", Label: "Before", Field: &formular.Field{Kind: formular.FieldText, Value: n.before, Readonly: true}},
7✔
272
                        {Type: formular.ItemField, ID: "after", Label: "After", Field: &formular.Field{Kind: formular.FieldText, Value: n.after, Readonly: true}},
7✔
273
                },
7✔
274
        }
7✔
275
}
7✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc