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

Support for Protocol Buffers #606

Open
armandino opened this issue May 11, 2023 · 5 comments
Open

Support for Protocol Buffers #606

armandino opened this issue May 11, 2023 · 5 comments
Labels
type: enhancement New feature or request up-for-grabs

Comments

@armandino
Copy link
Member

Feature description

Investigate adding support for protobufs, as suggested in the following discussion:

@armandino armandino added the type: investigation Investigation label May 11, 2023
@armandino armandino self-assigned this May 11, 2023
@FWest98
Copy link

FWest98 commented Aug 12, 2023

I haven't done a very thorough analysis, but the main problems seem to be with protobuf string fields. As mentioned in the discussion, these are internally represented as Objects, which is a dirty trick to let them use a "union type": the field is always either a String or a ByteString - and the getters convert between the two where relevant.

So far, it seems like these fields always have the name foo_ with getter function getFoo, so for my own usecase I am now using the following simple GeneratorProvider (note, this is Kotlin):

class ProtobufGeneratorProvider : InstancioServiceProvider.GeneratorProvider {
    override fun getGenerator(node: Node, generators: Generators): GeneratorSpec<*>? {
        val field = node.field ?: return null

        // Containing type check
        if(!GeneratedMessageV3::class.java.isAssignableFrom(field.declaringClass))
            return null

        // Check field type
        if(field.type != java.lang.Object::class.java)
            return null

        // Check field name -- we assume here that all protobuf generated internal fields
        // end with an underscore.
        if(!field.name.endsWith("_"))
            return null;

        // We assume in this case we have a generated string
        return generators.string()
    }
}

This can be made a bit more robust by checking the existence and presence of the appropriate getter method (getFoo) with proper return type String. However, I don't know whether this fully covers all cases.

Because of the annoying _ suffix, I also added a quick helper function to convert method references to the getter function to the correct backing field (can easily be adapted to Java):

fun <T : GeneratedMessageV3, V> KFunction1<T, V>.protoField(): TargetSelector {
    val fieldName = name.substringAfter("get").lowercase() + "_"
    return Select.field(fieldName)
}

As for an implementation in the main library, I guess it boils down to detecting the class being an extension of GeneratedMessageV3 (not sure about non-V3 implementations). Then maybe these checks can be done magically, as well as converting the field names appropriately.

@armandino
Copy link
Member Author

@FWest98 thank you for providing the analysis. It sounds like an interesting approach. Any chance you could put together a sample project? It might be useful as a starting point for others looking for a solution to this.

As for library support, I don't have a solution to this yet, but I'm keeping this issue open for now.

@armandino armandino added type: enhancement New feature or request status: in-progress Issue currently in progress and removed type: investigation Investigation labels Sep 29, 2023
@armandino
Copy link
Member Author

#767 added enhancements to AssignmentType.METHOD but those changes are not enough to handle protobuf builders. Currently, I don't have any good ideas for implementing this and I'm reluctant to add protobuf-specific logic to the core library.

Will keep the issue open in case somebody is interested in implementing this in a fork or as an extension project.

@DorianNaaji
Copy link

Protobuf support would indeed be an incredible nice to have. Proto strings as raised by @FWest98 are causing issues

armandino added a commit to instancio/instancio-github-issues that referenced this issue Mar 8, 2024
@armandino
Copy link
Member Author

@DorianNaaji protobuf strings can be created using a custom generator, e.g.

public class ByteStringGenerator implements Generator<ByteString> {
    @Override
    public ByteString generate(final Random random) {
        final int length = random.intRange(
                Keys.STRING_MIN_LENGTH.defaultValue(),
                Keys.STRING_MAX_LENGTH.defaultValue());

        return ByteString.copyFromUtf8(random.upperCaseAlphabetic(length));
    }

    @Override
    public Hints hints() {
        return Hints.afterGenerate(AfterGenerate.DO_NOT_MODIFY);
    }
}

I created a sample project here:

https://github.com/instancio/instancio-github-issues/tree/main/issue-606

The sample doesn't support:

  • generating com.google.protobuf.MapField
  • generating enums (they are represented as plain ints)
  • method reference selector (i.e. need to use field(Person.class, "name_") instead of field(Person::getName) due to field naming convention

Hope it helps as a starting point despite the limitations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement New feature or request up-for-grabs
Projects
None yet
Development

No branches or pull requests

3 participants