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

mlange-42 / arche-serde / 12457234164

22 Dec 2024 07:57PM CUT coverage: 95.808% (+0.1%) from 95.679%
12457234164

Pull #15

github

web-flow
Merge 13e02ce48 into e0568ef0d
Pull Request #15: Get rid of the use of deprecated methods

17 of 17 new or added lines in 1 file covered. (100.0%)

320 of 334 relevant lines covered (95.81%)

39.52 hits per line

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

91.52
/serialize.go
1
package archeserde
2

3
import (
4
        "encoding/json"
5
        "fmt"
6
        "reflect"
7
        "slices"
8
        "strings"
9

10
        "github.com/mlange-42/arche/ecs"
11
)
12

13
const targetTag = "arche.relation.Target"
14

15
// Serialize an Arche [ecs.World] to JSON.
16
//
17
// Serializes the following:
18
//   - Entities and the entity pool
19
//   - All components of all entities
20
//   - All resources
21
//
22
// All components and resources must be "JSON-able" with [encoding/json].
23
//
24
// The options can be used to skip some or all components,
25
// entities entirely, and/or some or all resources.
26
func Serialize(world *ecs.World, options ...Option) ([]byte, error) {
14✔
27
        opts := newSerdeOptions(options...)
14✔
28

14✔
29
        builder := strings.Builder{}
14✔
30

14✔
31
        builder.WriteString("{\n")
14✔
32

14✔
33
        if err := serializeWorld(world, &builder, &opts); err != nil {
14✔
34
                return nil, err
×
35
        }
×
36
        if !opts.skipEntities {
27✔
37
                builder.WriteString(",\n")
13✔
38
        }
13✔
39

40
        serializeTypes(world, &builder, &opts)
14✔
41
        builder.WriteString(",\n")
14✔
42

14✔
43
        if err := serializeComponents(world, &builder, &opts); err != nil {
14✔
44
                return nil, err
×
45
        }
×
46
        builder.WriteString(",\n")
14✔
47

14✔
48
        if err := serializeResources(world, &builder, &opts); err != nil {
14✔
49
                return nil, err
×
50
        }
×
51
        builder.WriteString("}\n")
14✔
52

14✔
53
        return []byte(builder.String()), nil
14✔
54
}
55

56
func serializeWorld(world *ecs.World, builder *strings.Builder, opts *serdeOptions) error {
14✔
57
        if opts.skipEntities {
15✔
58
                return nil
1✔
59
        }
1✔
60

61
        entities := world.DumpEntities()
13✔
62

13✔
63
        jsonData, err := json.Marshal(entities)
13✔
64
        if err != nil {
13✔
65
                return err
×
66
        }
×
67
        builder.WriteString(fmt.Sprintf("\"World\" : %s", string(jsonData)))
13✔
68
        return nil
13✔
69
}
70

71
func serializeTypes(world *ecs.World, builder *strings.Builder, opts *serdeOptions) {
14✔
72
        if opts.skipEntities || opts.skipAllComponents {
16✔
73
                builder.WriteString("\"Types\" : []")
2✔
74
                return
2✔
75
        }
2✔
76

77
        builder.WriteString("\"Types\" : [\n")
12✔
78

12✔
79
        types := map[ecs.ID]reflect.Type{}
12✔
80

12✔
81
        allComps := ecs.ComponentIDs(world)
12✔
82
        for _, id := range allComps {
44✔
83
                if info, ok := ecs.ComponentInfo(world, id); ok {
64✔
84
                        if !slices.Contains(opts.skipComponents, info.Type) {
63✔
85
                                types[id] = info.Type
31✔
86
                        }
31✔
87
                }
88
        }
89
        maxComp := len(types) - 1
12✔
90
        counter := 0
12✔
91
        for _, tp := range types {
43✔
92
                builder.WriteString(fmt.Sprintf("  \"%s\"", tp.String()))
31✔
93
                if counter < maxComp {
50✔
94
                        builder.WriteString(",")
19✔
95
                }
19✔
96
                builder.WriteString("\n")
31✔
97
                counter++
31✔
98
        }
99

100
        builder.WriteString("]")
12✔
101
}
102

103
func serializeComponents(world *ecs.World, builder *strings.Builder, opts *serdeOptions) error {
14✔
104
        if opts.skipEntities {
15✔
105
                builder.WriteString("\"Components\" : []")
1✔
106
                return nil
1✔
107
        }
1✔
108

109
        skipComponents := ecs.Mask{}
13✔
110
        for _, tp := range opts.skipComponents {
14✔
111
                id := ecs.TypeID(world, tp)
1✔
112
                skipComponents.Set(id, true)
1✔
113
        }
1✔
114

115
        builder.WriteString("\"Components\" : [\n")
13✔
116

13✔
117
        query := world.Query(ecs.All())
13✔
118
        lastEntity := query.Count() - 1
13✔
119
        counter := 0
13✔
120
        tempIDs := []ecs.ID{}
13✔
121
        for query.Next() {
109✔
122
                if opts.skipAllComponents {
99✔
123
                        builder.WriteString("  {")
3✔
124
                } else {
96✔
125
                        builder.WriteString("  {\n")
93✔
126

93✔
127
                        ids := query.Ids()
93✔
128

93✔
129
                        tempIDs = tempIDs[:0]
93✔
130
                        for _, id := range ids {
198✔
131
                                if !skipComponents.Get(id) {
208✔
132
                                        tempIDs = append(tempIDs, id)
103✔
133
                                }
103✔
134
                        }
135
                        last := len(tempIDs) - 1
93✔
136

93✔
137
                        for i, id := range tempIDs {
196✔
138
                                info, _ := ecs.ComponentInfo(world, id)
103✔
139

103✔
140
                                if info.IsRelation {
105✔
141
                                        target := query.Relation(id)
2✔
142
                                        eJSON, err := target.MarshalJSON()
2✔
143
                                        if err != nil {
2✔
144
                                                return err
×
145
                                        }
×
146
                                        builder.WriteString(fmt.Sprintf("    \"%s\" : %s,\n", targetTag, eJSON))
2✔
147
                                }
148

149
                                comp := query.Get(id)
103✔
150
                                value := reflect.NewAt(info.Type, comp).Interface()
103✔
151
                                jsonData, err := json.Marshal(value)
103✔
152
                                if err != nil {
103✔
153
                                        return err
×
154
                                }
×
155
                                builder.WriteString(fmt.Sprintf("    \"%s\" : ", info.Type.String()))
103✔
156
                                builder.WriteString(string(jsonData))
103✔
157
                                if i < last {
123✔
158
                                        builder.WriteString(",")
20✔
159
                                }
20✔
160
                                builder.WriteString("\n")
103✔
161
                        }
162
                }
163
                builder.WriteString("  }")
96✔
164
                if counter < lastEntity {
179✔
165
                        builder.WriteString(",")
83✔
166
                }
83✔
167
                builder.WriteString("\n")
96✔
168

96✔
169
                counter++
96✔
170
        }
171
        builder.WriteString("]")
13✔
172

13✔
173
        return nil
13✔
174
}
175

176
func serializeResources(world *ecs.World, builder *strings.Builder, opts *serdeOptions) error {
14✔
177
        if opts.skipAllResources {
15✔
178
                builder.WriteString("\"Resources\" : {}")
1✔
179
                return nil
1✔
180
        }
1✔
181

182
        builder.WriteString("\"Resources\" : {\n")
13✔
183

13✔
184
        resTypes := map[ecs.ResID]reflect.Type{}
13✔
185
        allRes := ecs.ResourceIDs(world)
13✔
186
        for _, id := range allRes {
33✔
187
                if tp, ok := ecs.ResourceType(world, id); ok {
40✔
188
                        if !slices.Contains(opts.skipResources, tp) {
39✔
189
                                resTypes[id] = tp
19✔
190
                        }
19✔
191
                }
192
        }
193

194
        last := len(resTypes) - 1
13✔
195
        counter := 0
13✔
196
        for id, tp := range resTypes {
32✔
197
                res := world.Resources().Get(id)
19✔
198
                rValue := reflect.ValueOf(res)
19✔
199
                ptr := rValue.UnsafePointer()
19✔
200

19✔
201
                value := reflect.NewAt(tp, ptr).Interface()
19✔
202
                jsonData, err := json.Marshal(value)
19✔
203
                if err != nil {
19✔
204
                        return err
×
205
                }
×
206

207
                builder.WriteString("    ")
19✔
208
                builder.WriteString(fmt.Sprintf("\"%s\" : ", tp.String()))
19✔
209
                builder.WriteString(string(jsonData))
19✔
210

19✔
211
                if counter < last {
28✔
212
                        builder.WriteString(",")
9✔
213
                }
9✔
214
                builder.WriteString("\n")
19✔
215
                counter++
19✔
216
        }
217

218
        builder.WriteString("}")
13✔
219

13✔
220
        return nil
13✔
221
}
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

© 2025 Coveralls, Inc