Skip to content

WIP: KMessages#7

Draft
louiseightsix wants to merge 1 commit intomainfrom
louis/kmessages-2
Draft

WIP: KMessages#7
louiseightsix wants to merge 1 commit intomainfrom
louis/kmessages-2

Conversation

@louiseightsix
Copy link
Collaborator

No description provided.

@louiseightsix louiseightsix changed the title WIP: Kasts WIP: KMessages Dec 10, 2023
Copy link
Contributor

@reidbuzby reidbuzby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main feedback I think is just in the API that the library exposes. Here's my take on what I think the API should look like:

  1. Define options in protobuf as you already have them. I think this looks pretty good as is
  2. Extend the KProtoMessage class for your use case (ideally KSP handles the basic case here). Ideally also this wouldn't have to include the Example.getDescriptor() in the constructor and this could be done with reflection.
  3. Call KExample.fromProto(protoMessage) when validating protos. I think exposing KMessages and having to call KMessages.defaultInstance().fromProto(...) is a bit confusing (but maybe that is necessary, although I think it could be avoided?)
  4. User can easily define a new validator option by extending an interface that looks something like:
// The generic on the ValidatorOption would help tell the validate function what to validate to/from somehow
class SomeOption : ValidatorOption<StringValidator>() {
  override val optionId = "some_option"

  // Not sure what the types would look like here, but ideally could be inferred from the 
  // type that gets passed into the ValidatorOption constructor
  override fun validate(value) {
    ...
  } 
}

I think we could keep all the proto validator types (like StringValidator, Int32Validator, etc) in the library and just hand write all those out so that every possible proto type is accounted for? That way users of the library only have to write new options for each validator if they want to add their own functionality?

Comment on lines +15 to +17
string string_default = 1 [
(InMemoryType) = "String"
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you omit the option entirely and have it infer the InMemoryType as a string?

Comment on lines +28 to +30
int32 int_default = 4 [
(InMemoryType) = "int32"
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, could this type be inferred?

Comment on lines +7 to +12
class KExample : KProtoMessage(Example.getDescriptor()) {
val string_default: String by ProtoField()
val string_nonempty: String by ProtoField()
val string_maxlength: String by ProtoField()
val int_default: Int by ProtoField()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you thinking this could be generated by KSP in the future?

}

class KExampleSubType1 : KExampleAdvanced() {
var string_nonempty: String by ProtoField()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if you don't want to use the ProtoField() delegate? Could you add a custom validator here?

intDefault = 456
}

val kExample: KExample? = KMessages.defaultInstance().fromProto(validExample)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you like this better than having a fromProto extension function on KProtoMessage?

Comment on lines +12 to +25
private val disallowEmpty: Boolean = options.hasOption<DisallowEmptyOption>()
private val maxLength: Int? = options.getOption<MaxLengthOption>()?.maxLength

override fun validate(value: String): ValidationResult {
if (disallowEmpty && value.isEmpty())
return ValidationResult.Invalid("Empty strings not allowed, but string was empty")

if (maxLength != null && value.length > maxLength)
return ValidationResult.Invalid(
"""Max string length is [${maxLength}],
but string length was [${value.length}]""".trimMargin()
)

return ValidationResult.Valid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think there's a way to automatically check each option without having to hand write out each one? I'm thinking if someone wants to add a new String validator option like UUIDOnly or something wouldn't they also have to update this class? Which would currently not be possible since this lives in the library?

import kotlin.reflect.KProperty

abstract class KProtoMessage(
private val descriptor: Descriptor,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the proto message type be passed into the constructor and the descriptor could be pulled off of the type with reflection? So the api would look like:

class KExample : KProtoMessage<Example>() {
 ...
}

Comment on lines +49 to +51
KMessages().apply {
registerValidator(StringValidator::class)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the registered validators is going to be a hardcoded set of validators that lives only in the library, could we just define the validators and validatorOptions as a hardcoded list? That way you don't have to call KMessages.defaultInstance() ever?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants