Coverage Report

Created: 2026-06-30 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-protobuf/Sources/SwiftProtobuf/JSONMapEncodingVisitor.swift
Line
Count
Source
1
// Sources/SwiftProtobuf/JSONMapEncodingVisitor.swift - JSON map encoding visitor
2
//
3
// Copyright (c) 2014 - 2016 Apple Inc. and the project authors
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
//
6
// See LICENSE.txt for license information:
7
// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
8
//
9
// -----------------------------------------------------------------------------
10
///
11
/// Visitor that writes out the key/value pairs for a JSON map.
12
///
13
// -----------------------------------------------------------------------------
14
15
#if canImport(FoundationEssentials)
16
import FoundationEssentials
17
#else
18
import Foundation
19
#endif
20
21
/// Visitor that serializes a message into JSON map format.
22
///
23
/// This expects to alternately visit the keys and values for a JSON
24
/// map.  It only accepts singular values.  Keys should be identified
25
/// as `fieldNumber:1`, values should be identified as `fieldNumber:2`
26
///
27
internal struct JSONMapEncodingVisitor: SelectiveVisitor {
28
    private var separator: StaticString?
29
    internal var encoder: JSONEncoder
30
    private let options: JSONEncodingOptions
31
32
53.4k
    init(encoder: JSONEncoder, options: JSONEncodingOptions) {
33
53.4k
        self.encoder = encoder
34
53.4k
        self.options = options
35
53.4k
    }
36
37
    internal var bytesResult: [UInt8] {
38
12.5k
        get {
39
12.5k
            encoder.bytesResult
40
12.5k
        }
41
    }
42
43
87.6k
    private mutating func startKey() {
44
87.6k
        if let s = separator {
45
27.3k
            encoder.append(staticText: s)
46
60.3k
        } else {
47
60.3k
            separator = ","
48
60.3k
        }
49
87.6k
    }
50
51
87.6k
    private mutating func startValue() {
52
87.6k
        encoder.append(staticText: ":")
53
87.6k
    }
54
55
904
    mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
56
904
        // Doubles/Floats can never be map keys, only values
57
904
        assert(fieldNumber == 2)
58
904
        startValue()
59
904
        encoder.putFloatValue(value: value)
60
904
    }
61
62
918
    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
63
918
        // Doubles/Floats can never be map keys, only values
64
918
        assert(fieldNumber == 2)
65
918
        startValue()
66
918
        encoder.putDoubleValue(value: value)
67
918
    }
68
69
10.5k
    mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
70
10.5k
        if fieldNumber == 1 {
71
5.86k
            startKey()
72
5.86k
            encoder.putQuotedInt32(value: value)
73
5.86k
        } else {
74
4.69k
            startValue()
75
4.69k
            encoder.putNonQuotedInt32(value: value)
76
4.69k
        }
77
10.5k
    }
78
79
9.19k
    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
80
9.19k
        if fieldNumber == 1 {
81
5.81k
            startKey()
82
5.81k
            encoder.putQuotedInt64(value: value)
83
5.81k
        } else {
84
3.38k
            startValue()
85
3.38k
            options.alwaysPrintInt64sAsNumbers
86
3.38k
                ? encoder.putNonQuotedInt64(value: value)
87
3.38k
                : encoder.putQuotedInt64(value: value)
88
3.38k
        }
89
9.19k
    }
90
91
6.79k
    mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
92
6.79k
        if fieldNumber == 1 {
93
4.04k
            startKey()
94
4.04k
            encoder.putQuotedUInt32(value: value)
95
4.04k
        } else {
96
2.75k
            startValue()
97
2.75k
            encoder.putNonQuotedUInt32(value: value)
98
2.75k
        }
99
6.79k
    }
100
101
5.44k
    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
102
5.44k
        if fieldNumber == 1 {
103
2.66k
            startKey()
104
2.66k
            encoder.putQuotedUInt64(value: value)
105
2.77k
        } else {
106
2.77k
            startValue()
107
2.77k
            options.alwaysPrintInt64sAsNumbers
108
2.77k
                ? encoder.putNonQuotedUInt64(value: value)
109
2.77k
                : encoder.putQuotedUInt64(value: value)
110
2.77k
        }
111
5.44k
    }
112
113
3.60k
    mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
114
3.60k
        try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
115
3.60k
    }
116
117
4.45k
    mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
118
4.45k
        try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
119
4.45k
    }
120
121
4.35k
    mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
122
4.35k
        try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber)
123
4.35k
    }
124
125
2.66k
    mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
126
2.66k
        try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber)
127
2.66k
    }
128
129
2.43k
    mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
130
2.43k
        try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
131
2.43k
    }
132
133
1.32k
    mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
134
1.32k
        try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
135
1.32k
    }
136
137
683
    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
138
683
        if fieldNumber == 1 {
139
683
            startKey()
140
683
            encoder.putQuotedBoolValue(value: value)
141
683
        } else {
142
0
            startValue()
143
0
            encoder.putNonQuotedBoolValue(value: value)
144
0
        }
145
683
    }
146
147
3.55k
    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
148
3.55k
        if fieldNumber == 1 {
149
2.99k
            startKey()
150
2.99k
        } else {
151
564
            startValue()
152
564
        }
153
3.55k
        encoder.putStringValue(value: value)
154
3.55k
    }
155
156
1.33k
    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
157
1.33k
        // Bytes can only be map values, never keys
158
1.97k
        assert(fieldNumber == 2)
159
1.33k
        startValue()
160
1.33k
        encoder.putBytesValue(value: value)
161
1.33k
    }
162
163
782
    mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
164
782
        // Enums can only be map values, never keys
165
782
        assert(fieldNumber == 2)
166
782
        startValue()
167
782
        if !options.alwaysPrintEnumsAsInts, let n = value.name {
168
650
            encoder.putStringValue(value: String(describing: n))
169
650
        } else {
170
132
            encoder.putEnumInt(value: value.rawValue)
171
132
        }
172
782
    }
173
174
8.52k
    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
175
8.52k
        // Messages can only be map values, never keys
176
8.52k
        assert(fieldNumber == 2)
177
8.52k
        startValue()
178
8.52k
        let json = try value.jsonString(options: options)
179
8.52k
        encoder.append(text: json)
180
8.52k
    }
181
182
    // SelectiveVisitor will block:
183
    // - single Groups
184
    // - everything repeated
185
    // - everything packed
186
    // - all maps
187
    // - unknown fields
188
    // - extensions
189
}