Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[swift] Provide a default value for sub fields and common types as per proto spec #2650

Merged
merged 1 commit into from
Oct 13, 2023

Conversation

dnkoutso
Copy link
Collaborator

@dnkoutso dnkoutso commented Sep 23, 2023

This implements the proto spec:

If the default value is not specified for an optional element, a type-specific default value is used instead: for strings, the default value is the empty string. For bytes, the default value is the empty byte string. For bools, the default value is false. For numeric types, the default value is zero. For enums, the default value is the first value listed in the enum’s type definition. This means care must be taken when adding a value to the beginning of an enum value list. See the Updating A Message Type section for guidelines on how to safely change definitions.

@dnkoutso dnkoutso changed the title [swift] WIP Generate a default value for sub fields with only optional fields [swift] Generate a default value for sub fields with only optional fields Sep 25, 2023
@dnkoutso dnkoutso force-pushed the default_values_test branch 2 times, most recently from 35a6331 to b1b41ec Compare October 2, 2023 17:22
@dnkoutso dnkoutso force-pushed the default_values_test branch 2 times, most recently from 4f6ba0c to 8e7fb83 Compare October 11, 2023 14:53
@dnkoutso dnkoutso changed the title [swift] Generate a default value for sub fields with only optional fields [swift] Provide a default value for sub fields and common types as per proto spec Oct 11, 2023
@dnkoutso dnkoutso force-pushed the default_values_test branch 5 times, most recently from 6db6816 to a4af441 Compare October 11, 2023 20:23
@dnkoutso dnkoutso force-pushed the default_values_test branch 3 times, most recently from f009d6e to a7bdfd8 Compare October 11, 2023 23:06
Copy link
Collaborator

@lickel lickel left a comment

Choose a reason for hiding this comment

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

This is effectively doubling the storage requirements for everything...
It's now making me a bit nervous...

Maybe we can do something more like BetterCodable and use statics for String, Number, etc...

The empty structs could also be solved by conforming to a protocol like EmptyInitializable

@propertyWrapper
public struct DefaultedZero<Value: Numeric> {
    public var wrappedValue: Value?
     
    public var projectedValue: Value {
        wrappedValue ?? .zero
    }
}

@propertyWrapper
public struct DefaultedEmpty<Value: EmptyInitializable> {
    public var wrappedValue: Value?

    public var projectedValue: Value {
        wrappedValue ?? Value.init()
    }
}

@@ -8,13 +8,17 @@ public struct Dinosaur {
/**
* Common name of this dinosaur, like "Stegosaurus".
*/
@Defaulted(defaultValue: "")
Copy link
Collaborator

@lickel lickel Oct 11, 2023

Choose a reason for hiding this comment

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

  • Should probably update the README to include info about @defaulted and this updated model

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeap, thanks for reminding

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

}
if (isEnum) {
val enumType = schema.getType(type!!) as EnumType
return if (enumType.constants.getOrNull(0) != null) CodeBlock.of("%T.%L", typeName.makeNonOptional(), enumType.constants[0].name) else null
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is enumType.constants.getOrNull(0) correct? That just looks like the first (mapped) constant, not the 0-th value.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Based on the proto3 code, I think we want enum.constants.filter { it.tag == 0 }.firstOrNull()

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes this is probably incorrect. There is also enum.constant(0) but I think this didnt work, will be updating and looking again.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

DOUBLE -> return CodeBlock.of("%L", 0)
STRING -> return CodeBlock.of("%S", "")
DATA -> return CodeBlock.of(
"%T(base64Encoded: %S)!",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you not just do Data() ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah it was an initial copy paste from below will be updating

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

@lickel
Copy link
Collaborator

lickel commented Oct 11, 2023

Discussed offline: we need Defaulted to have conditional conformance to Redactable

@dnkoutso dnkoutso force-pushed the default_values_test branch 5 times, most recently from f2772d1 to e6a251e Compare October 12, 2023 16:43
@@ -44,16 +44,20 @@ extension Redactable {
guard let label = label else {
return "\(value)"
}
if RedactedKeys(rawValue: label) != nil {
var strippedLabel = label
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I believe this is an existing bug today. If a custom default value was specified due to applying @Defaulted the internal property is renamed with a _ in front. This fails this check.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Test caught this now because we emit @Defaulted identity types for many properties which now as an empty string exacerbated this issue which is now fixed and the test passes.

@dnkoutso dnkoutso force-pushed the default_values_test branch from e6a251e to e1c4eaa Compare October 12, 2023 18:47
@dnkoutso dnkoutso force-pushed the default_values_test branch 2 times, most recently from 7a8f922 to dd5aafe Compare October 13, 2023 00:17
@dnkoutso dnkoutso force-pushed the default_values_test branch from dd5aafe to db6b654 Compare October 13, 2023 00:22
@dnkoutso dnkoutso merged commit b0c6e13 into master Oct 13, 2023
11 of 13 checks passed
@dnkoutso dnkoutso deleted the default_values_test branch October 13, 2023 01:12
@jamieQ
Copy link

jamieQ commented Oct 13, 2023

Discussed offline: we need Defaulted to have conditional conformance to Redactable

before this falls out of my memory: the conditional conformance did not appear necessary to address the test failures occurring with the nested, partially redacted types. the reason being that the Redactable conformance is present on the types the Defaulted property wrapper applies to, and that appears to be what the Mirror actually sees when constructing the recursive description (vs seeing the property wrapper type). e.g. the Mirror iteration logic sees _defaultedProp: SomeRedactableType vs defaultedProp: Defaulted<SomeRedactableType>, or at least that was my impression of what was happening.

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.

4 participants