or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

any-message.mdenum-support.mdfield-annotations.mdindex.mdmessage-framework.mdproto-adapters.mdprotobuf-io.mdtime-types.md

enum-support.mddocs/

0

# Enum Support

1

2

Support for protocol buffer enums with proper value mapping, unknown value handling, and cross-platform compatibility. Wire's enum system provides type-safe enum constants while maintaining protobuf wire compatibility.

3

4

## Capabilities

5

6

### WireEnum Interface

7

8

Interface that all generated enum types implement to provide their integer tag values.

9

10

```kotlin { .api }

11

/**

12

* Interface for generated enum values to help serialization and deserialization

13

*/

14

interface WireEnum {

15

/** The tag value of an enum constant */

16

val value: Int

17

}

18

```

19

20

**Usage Examples:**

21

22

```kotlin

23

// Generated enum implementing WireEnum

24

enum class Status(override val value: Int) : WireEnum {

25

UNKNOWN(0),

26

ACTIVE(1),

27

INACTIVE(2),

28

PENDING(3);

29

30

companion object {

31

fun fromValue(value: Int): Status? = when (value) {

32

0 -> UNKNOWN

33

1 -> ACTIVE

34

2 -> INACTIVE

35

3 -> PENDING

36

else -> null

37

}

38

}

39

}

40

41

// Usage

42

val status = Status.ACTIVE

43

println(status.value) // Prints: 1

44

45

// Wire format uses integer values

46

val encoded = StatusAdapter.encode(status) // Encodes as varint 1

47

val decoded = StatusAdapter.decode(encoded) // Decodes back to ACTIVE

48

```

49

50

### EnumAdapter Abstract Class

51

52

Abstract base class for enum adapters that converts enum values to and from their integer representations.

53

54

```kotlin { .api }

55

/**

56

* Abstract ProtoAdapter that converts enum values to and from integers

57

* @param E The enum type extending WireEnum

58

* @param type The Kotlin class of the enum

59

* @param syntax Proto syntax version (proto2 or proto3)

60

* @param identity Identity value for proto3 (typically the zero value)

61

*/

62

expect abstract class EnumAdapter<E : WireEnum> protected constructor(

63

type: KClass<E>,

64

syntax: Syntax,

65

identity: E?

66

) : ProtoAdapter<E> {

67

/** Size of encoded enum value (varint encoding) */

68

override fun encodedSize(value: E): Int

69

70

/** Encode enum as varint */

71

@Throws(IOException::class)

72

override fun encode(writer: ProtoWriter, value: E)

73

74

/** Encode enum as varint (reverse writer) */

75

@Throws(IOException::class)

76

override fun encode(writer: ReverseProtoWriter, value: E)

77

78

/** Decode varint to enum, throw exception for unknown values */

79

@Throws(IOException::class)

80

override fun decode(reader: ProtoReader): E

81

82

/** Return redacted enum (throws UnsupportedOperationException) */

83

override fun redact(value: E): E

84

85

/**

86

* Convert integer to enum value

87

* @param value Integer value from wire format

88

* @return Enum constant or null if unknown

89

*/

90

protected abstract fun fromValue(value: Int): E?

91

}

92

```

93

94

**Implementation Examples:**

95

96

```kotlin

97

// Generated enum adapter

98

object StatusAdapter : EnumAdapter<Status>(

99

Status::class,

100

Syntax.PROTO_2,

101

Status.UNKNOWN

102

) {

103

override fun fromValue(value: Int): Status? = Status.fromValue(value)

104

}

105

106

// Usage with messages

107

class UserMessage(

108

val name: String,

109

val status: Status,

110

unknownFields: ByteString = ByteString.EMPTY

111

) : Message<UserMessage, UserMessage.Builder>(ADAPTER, unknownFields) {

112

113

companion object {

114

@JvmField

115

val ADAPTER = object : ProtoAdapter<UserMessage>(

116

FieldEncoding.LENGTH_DELIMITED,

117

UserMessage::class,

118

null,

119

Syntax.PROTO_2

120

) {

121

override fun encodedSize(value: UserMessage): Int {

122

return ProtoAdapter.STRING.encodedSizeWithTag(1, value.name) +

123

StatusAdapter.encodedSizeWithTag(2, value.status)

124

}

125

126

override fun encode(writer: ProtoWriter, value: UserMessage) {

127

ProtoAdapter.STRING.encodeWithTag(writer, 1, value.name)

128

StatusAdapter.encodeWithTag(writer, 2, value.status)

129

}

130

131

override fun decode(reader: ProtoReader): UserMessage {

132

var name = ""

133

var status = Status.UNKNOWN

134

135

reader.forEachTag { tag ->

136

when (tag) {

137

1 -> name = ProtoAdapter.STRING.decode(reader)

138

2 -> status = StatusAdapter.decode(reader)

139

else -> reader.readUnknownField(tag)

140

}

141

}

142

143

return UserMessage(name, status)

144

}

145

146

override fun redact(value: UserMessage) = value

147

}

148

}

149

}

150

```

151

152

### Unknown Enum Value Handling

153

154

Wire handles unknown enum values by throwing `EnumConstantNotFoundException` for type safety.

155

156

```kotlin { .api }

157

// Exception for unknown enum values

158

class ProtoAdapter.EnumConstantNotFoundException(

159

value: Int,

160

type: KClass<*>?

161

) : IllegalArgumentException {

162

@JvmField

163

val value: Int

164

}

165

```

166

167

**Handling Unknown Enum Values:**

168

169

```kotlin

170

import com.squareup.wire.ProtoAdapter.EnumConstantNotFoundException

171

172

// Reading message with potentially unknown enum values

173

try {

174

val message = UserMessage.ADAPTER.decode(wireData)

175

println("Status: ${message.status}")

176

} catch (e: EnumConstantNotFoundException) {

177

println("Unknown enum value: ${e.value}")

178

// Handle unknown enum gracefully

179

// Option 1: Use default value

180

val defaultMessage = UserMessage("Unknown User", Status.UNKNOWN)

181

182

// Option 2: Skip processing this message

183

return

184

185

// Option 3: Store unknown value for later processing

186

// (requires custom adapter implementation)

187

}

188

189

// Custom adapter that preserves unknown enum values

190

object SafeStatusAdapter : ProtoAdapter<Status?>(

191

FieldEncoding.VARINT,

192

Status::class,

193

null,

194

Syntax.PROTO_2,

195

null

196

) {

197

override fun encodedSize(value: Status?): Int {

198

return if (value != null) {

199

ProtoWriter.varint32Size(value.value)

200

} else 0

201

}

202

203

override fun encode(writer: ProtoWriter, value: Status?) {

204

if (value != null) {

205

writer.writeVarint32(value.value)

206

}

207

}

208

209

override fun decode(reader: ProtoReader): Status? {

210

val intValue = reader.readVarint32()

211

return Status.fromValue(intValue) // Returns null for unknown values

212

}

213

214

override fun redact(value: Status?): Status? = throw UnsupportedOperationException()

215

}

216

```

217

218

### Proto2 vs Proto3 Enum Behavior

219

220

Enum handling differs between proto2 and proto3 syntax:

221

222

```kotlin

223

// Proto2 enum (closed set, unknown values cause exceptions)

224

enum class Proto2Status(override val value: Int) : WireEnum {

225

UNKNOWN(0),

226

ACTIVE(1),

227

INACTIVE(2);

228

229

companion object {

230

val ADAPTER = object : EnumAdapter<Proto2Status>(

231

Proto2Status::class,

232

Syntax.PROTO_2,

233

UNKNOWN

234

) {

235

override fun fromValue(value: Int): Proto2Status? = when (value) {

236

0 -> UNKNOWN

237

1 -> ACTIVE

238

2 -> INACTIVE

239

else -> null // Unknown values not allowed in proto2

240

}

241

}

242

}

243

}

244

245

// Proto3 enum (open set, identity value omitted)

246

enum class Proto3Status(override val value: Int) : WireEnum {

247

UNSPECIFIED(0), // Proto3 requires zero value

248

ACTIVE(1),

249

INACTIVE(2);

250

251

companion object {

252

val ADAPTER = object : EnumAdapter<Proto3Status>(

253

Proto3Status::class,

254

Syntax.PROTO_3,

255

UNSPECIFIED // Identity value omitted when encoding

256

) {

257

override fun fromValue(value: Int): Proto3Status? = when (value) {

258

0 -> UNSPECIFIED

259

1 -> ACTIVE

260

2 -> INACTIVE

261

else -> null

262

}

263

}

264

}

265

}

266

267

// Proto3 identity handling

268

val message = SomeMessage.Builder()

269

.status(Proto3Status.UNSPECIFIED) // This won't be encoded (identity value)

270

.build()

271

272

val encoded = message.encode() // status field not present in wire format

273

val decoded = SomeMessage.ADAPTER.decode(encoded) // status defaults to UNSPECIFIED

274

```

275

276

### Enum Annotations

277

278

Wire supports annotations for enum constants to control code generation:

279

280

```kotlin { .api }

281

/**

282

* Annotation for enum constants

283

*/

284

@Target(AnnotationTarget.FIELD)

285

@Retention(AnnotationRetention.RUNTIME)

286

annotation class WireEnumConstant(

287

/** Override the constant name in generated code */

288

val declaredName: String = ""

289

)

290

```

291

292

### Performance Characteristics

293

294

- **Encoding**: Enums are encoded as varints, making small values (0-127) very efficient

295

- **Decoding**: Direct integer-to-enum mapping with O(1) lookups

296

- **Memory**: No boxing overhead for enum values

297

- **Type Safety**: Compile-time checking prevents invalid enum assignments

298

299

### Best Practices

300

301

1. **Always include a zero value** for proto3 enums (required by spec)

302

2. **Use meaningful names** for enum constants that describe their purpose

303

3. **Handle unknown values gracefully** in production code

304

4. **Don't rely on enum ordinal values** - use the `value` property instead

305

5. **Consider backwards compatibility** when adding new enum values

306

6. **Use consistent naming** between proto file and generated Kotlin enums

307

308

### Cross-Platform Consistency

309

310

Wire ensures enum behavior is consistent across all supported platforms:

311

312

- **JVM**: Full enum class support with proper `toString()` and comparison

313

- **JavaScript**: Enum-like objects with integer values

314

- **Native**: Efficient enum representations for each target platform

315

- **Wire Format**: Identical binary encoding across all platforms