Skip to content

Conversation

@Jozott00
Copy link
Collaborator

@Jozott00 Jozott00 commented Dec 1, 2025

Subsystem
Protobuf

Problem Description
Even if a field in a message is optional, we can't make it nullable. This is because fields may either have a default value, or we want to be able to access a default value in a nested field such that the parent object must not be null.

However, users must be able to distinguish between a field that was manually set with the default value and one that was not actively set.

Solution
This PR solves this problem by adding a presence API to each message that contains fields with presence tracking.

val myMessage = Message { myField = "value" }
myMessage.presence.hasField

presence is an extension getter on the generated message interface that provides a hasXYZ getter for all fields with tracked/explicit presence.
This intermediate presence object is employed to ensure concise code completion and minimize the number of suggestions provided by the IDE.

The only field type that lacks a hasXYZ getter while its presence is tracked is the oneof field. Since oneof fields are nullable, they are null if they are not set.

@Jozott00 Jozott00 self-assigned this Dec 1, 2025
@Jozott00 Jozott00 added the feature New feature or request label Dec 1, 2025
@Jozott00 Jozott00 force-pushed the grpc/presence-check branch from c4b2dc3 to 7202c9a Compare December 1, 2025 13:06
@Jozott00 Jozott00 requested a review from Mr3zee December 1, 2025 13:07
@Mr3zee
Copy link
Member

Mr3zee commented Dec 1, 2025

@Jozott00 great and quick job!
I won't have time to look into it closely today, but I noticed two things after a quick glance:

  • Presence class that is generated is in __rpc_internal package, which is fine for it's implementation, but I think we should expose to the user an interface with hasField properties in .ext.kt and keep the impl for it in __rpc_internal, so that the navigation doesn't bring you to internal when possible
  • No generated docs for .presence

@Jozott00
Copy link
Collaborator Author

Jozott00 commented Dec 1, 2025

@Mr3zee

  1. Yes, that is probably better. Any suggestions on how to name the interface? Currently it is an inner class which makes it easy. When moving it to the .ext.kt should I name the interface <OuterClass><InnerClass>Presence? (assuming it's the interface for a nested message)
  2. Good point, I will add documentation

@Mr3zee
Copy link
Member

Mr3zee commented Dec 1, 2025

@Jozott00 good question, I don't know 😄 Extension classes would be great here

@Mr3zee
Copy link
Member

Mr3zee commented Dec 1, 2025

what about

interface OuterClassPresence {
    val hasField: Boolean
	
    interface InnerClass {
        val hasInnerMessageField: Boolean 
    }
}

@Mr3zee
Copy link
Member

Mr3zee commented Dec 1, 2025

Also, please update the conformance regeneration

@Jozott00 Jozott00 force-pushed the grpc/presence-check branch from 3f352f5 to f6d343c Compare December 2, 2025 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants