Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions fixtures/edition2023.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Top-level comments are attached to the edition directive.
edition = "2023";

import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
import "extend.proto";
option go_package = "edition2023";

// The official documentation for the Edition 2023 test.
//
// This file demonstrates edition 2023 syntax and features,
// including explicit field presence by default.
package com.pseudomuto.protokit.edition2023;

option (com.pseudomuto.protokit.v1.extend_file) = true;

// A service for testing edition 2023 features.
service Edition2023Service {
option (com.pseudomuto.protokit.v1.extend_service) = true;

// Test method for edition 2023
rpc TestMethod(TestRequest) returns (TestResponse) {
option (com.pseudomuto.protokit.v1.extend_method) = true;
}
}

// Test enumeration for edition 2023
enum TestEnum {
option (com.pseudomuto.protokit.v1.extend_enum) = true;

UNKNOWN = 0; // Unknown value
VALUE_A = 1 [(com.pseudomuto.protokit.v1.extend_enum_value) = true]; // First value
VALUE_B = 2; // Second value
}

// Test message for edition 2023 with explicit field presence by default
message TestMessage {
option (com.pseudomuto.protokit.v1.extend_message) = true;

int64 id = 1; // Message ID with explicit presence
string name = 2 [(com.pseudomuto.protokit.v1.extend_field) = true]; // Message name
TestEnum type = 3; // Message type
google.protobuf.Timestamp created_at = 4; // Creation timestamp
google.protobuf.Any metadata = 5; // Additional metadata
}

// Request message for testing
message TestRequest {
string query = 1; // Search query
int32 limit = 2; // Result limit
}

// Response message for testing
message TestResponse {
repeated TestMessage results = 1; // Search results
int32 total_count = 2; // Total result count
}
56 changes: 56 additions & 0 deletions fixtures/edition2023_implicit.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Edition 2023 with implicit field presence (proto3-like semantics)
edition = "2023";

import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
import "extend.proto";

option go_package = "edition2023_implicit";
option features.field_presence = IMPLICIT;

// Test package with proto3-like semantics using editions
package com.pseudomuto.protokit.edition2023.implicit;

option (com.pseudomuto.protokit.v1.extend_file) = true;

// A service for testing edition 2023 with implicit field presence
service Edition2023ImplicitService {
option (com.pseudomuto.protokit.v1.extend_service) = true;

// Test method for edition 2023 implicit
rpc TestMethod(TestRequest) returns (TestResponse) {
option (com.pseudomuto.protokit.v1.extend_method) = true;
}
}

// Test enumeration for edition 2023 implicit
enum TestEnum {
option (com.pseudomuto.protokit.v1.extend_enum) = true;

UNKNOWN = 0; // Unknown value
VALUE_A = 1 [(com.pseudomuto.protokit.v1.extend_enum_value) = true]; // First value
VALUE_B = 2; // Second value
}

// Test message for edition 2023 with implicit field presence (like proto3)
message TestMessage {
option (com.pseudomuto.protokit.v1.extend_message) = true;

int64 id = 1; // Message ID with implicit presence
string name = 2 [(com.pseudomuto.protokit.v1.extend_field) = true]; // Message name
TestEnum type = 3; // Message type
google.protobuf.Timestamp created_at = 4; // Creation timestamp
google.protobuf.Any metadata = 5; // Additional metadata
}

// Request message for testing
message TestRequest {
string query = 1; // Search query
int32 limit = 2; // Result limit
}

// Response message for testing
message TestResponse {
repeated TestMessage results = 1; // Search results
int32 total_count = 2; // Total result count
}
71 changes: 71 additions & 0 deletions fixtures/edition2024.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Top-level comments are attached to the edition directive.
edition = "2024";

import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "extend.proto";
option go_package = "edition2024";

// The official documentation for the Edition 2024 test.
//
// This file demonstrates edition 2024 syntax and features,
// including enhanced symbol visibility controls.
package com.pseudomuto.protokit.edition2024;

option (com.pseudomuto.protokit.v1.extend_file) = true;

// A service for testing edition 2024 features.
service Edition2024Service {
option (com.pseudomuto.protokit.v1.extend_service) = true;

// Test method for edition 2024
rpc TestMethod(TestRequest) returns (TestResponse) {
option (com.pseudomuto.protokit.v1.extend_method) = true;
}

// Another test method
rpc AnotherMethod(TestRequest) returns (TestResponse);
}

// Test enumeration for edition 2024
enum TestEnum {
option (com.pseudomuto.protokit.v1.extend_enum) = true;

UNKNOWN = 0; // Unknown value
OPTION_X = 1 [(com.pseudomuto.protokit.v1.extend_enum_value) = true]; // First option
OPTION_Y = 2; // Second option
OPTION_Z = 3; // Third option
}

// Test message for edition 2024
message TestMessage {
option (com.pseudomuto.protokit.v1.extend_message) = true;

int64 id = 1; // Message ID
string title = 2 [(com.pseudomuto.protokit.v1.extend_field) = true]; // Message title
TestEnum category = 3; // Message category
google.protobuf.Duration timeout = 4; // Timeout duration
google.protobuf.Any payload = 5; // Message payload

// Nested message for testing
message NestedData {
string key = 1; // Data key
bytes value = 2; // Data value
}

repeated NestedData data = 6; // Nested data entries
}

// Request message for testing
message TestRequest {
string filter = 1; // Filter criteria
int32 page_size = 2; // Page size
string page_token = 3; // Page token for pagination
}

// Response message for testing
message TestResponse {
repeated TestMessage items = 1; // Response items
string next_page_token = 2; // Next page token
int64 total_size = 3; // Total items available
}
Binary file modified fixtures/fileset.pb
Binary file not shown.
2 changes: 1 addition & 1 deletion fixtures/generate.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package main

//go:generate protoc --descriptor_set_out=fileset.pb --include_imports --include_source_info -I. ./booking.proto ./todo.proto ./extend.proto
//go:generate protoc --descriptor_set_out=fileset.pb --include_imports --include_source_info -I. ./booking.proto ./todo.proto ./extend.proto ./edition2023.proto ./edition2024.proto ./edition2023_implicit.proto
2 changes: 2 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
serviceCommentPath = 6
extensionCommentPath = 7
syntaxCommentPath = 12
editionCommentPath = 14

// tag numbers in DescriptorProto
messageFieldCommentPath = 2 // field
Expand Down Expand Up @@ -62,6 +63,7 @@ func parseFile(ctx context.Context, fd *descriptorpb.FileDescriptorProto) *FileD
FileDescriptorProto: fd,
PackageComments: comments.Get(strconv.Itoa(packageCommentPath)),
SyntaxComments: comments.Get(strconv.Itoa(syntaxCommentPath)),
EditionComments: comments.Get(strconv.Itoa(editionCommentPath)),
}

if fd.Options != nil {
Expand Down
135 changes: 135 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,138 @@ func TestExtendedOptions(t *testing.T) {
require.True(t, ok)
require.True(t, *extendedValue)
}

func setupEditionsTest(t *testing.T) (*protokit.FileDescriptor, *protokit.FileDescriptor) {
set, err := utils.LoadDescriptorSet("fixtures", "fileset.pb")
require.NoError(t, err)

req := utils.CreateGenRequest(set, "edition2023.proto", "edition2024.proto")
files := protokit.ParseCodeGenRequest(req)
edition2023 := files[0]
edition2024 := files[1]

return edition2023, edition2024
}

func TestEditionsParsing(t *testing.T) {
t.Parallel()

edition2023, edition2024 := setupEditionsTest(t)

// Test edition 2023
require.True(t, edition2023.IsEditions())
require.False(t, edition2023.IsProto3())
require.Equal(t, "2023", edition2023.GetEditionName())
require.Equal(t, "editions", edition2023.GetSyntaxType())
require.True(t, edition2023.HasExplicitFieldPresence())
require.Equal(t, "Top-level comments are attached to the edition directive.", edition2023.GetSyntaxComments().String())
require.Contains(t, edition2023.GetPackageComments().String(), "The official documentation for the Edition 2023 test.")

// Test edition 2024
require.True(t, edition2024.IsEditions())
require.False(t, edition2024.IsProto3())
require.Equal(t, "2024", edition2024.GetEditionName())
require.Equal(t, "editions", edition2024.GetSyntaxType())
require.True(t, edition2024.HasExplicitFieldPresence())
require.Equal(t, "Top-level comments are attached to the edition directive.", edition2024.GetSyntaxComments().String())
require.Contains(t, edition2024.GetPackageComments().String(), "The official documentation for the Edition 2024 test.")
}

func TestEditionsServices(t *testing.T) {
t.Parallel()

edition2023, edition2024 := setupEditionsTest(t)

// Test edition 2023 service
require.Len(t, edition2023.GetServices(), 1)
svc2023 := edition2023.GetService("Edition2023Service")
require.NotNil(t, svc2023)
require.True(t, svc2023.IsEditions())
require.False(t, svc2023.IsProto3())
require.Len(t, svc2023.GetMethods(), 1)

// Test edition 2024 service
require.Len(t, edition2024.GetServices(), 1)
svc2024 := edition2024.GetService("Edition2024Service")
require.NotNil(t, svc2024)
require.True(t, svc2024.IsEditions())
require.False(t, svc2024.IsProto3())
require.Len(t, svc2024.GetMethods(), 2)
}

func TestEditionsEnums(t *testing.T) {
t.Parallel()

edition2023, edition2024 := setupEditionsTest(t)

// Test edition 2023 enum
require.Len(t, edition2023.GetEnums(), 1)
enum2023 := edition2023.GetEnum("TestEnum")
require.NotNil(t, enum2023)
require.True(t, enum2023.IsEditions())
require.False(t, enum2023.IsProto3())

// Test edition 2024 enum
require.Len(t, edition2024.GetEnums(), 1)
enum2024 := edition2024.GetEnum("TestEnum")
require.NotNil(t, enum2024)
require.True(t, enum2024.IsEditions())
require.False(t, enum2024.IsProto3())
}

func TestEditionsMessages(t *testing.T) {
t.Parallel()

edition2023, edition2024 := setupEditionsTest(t)

// Test edition 2023 messages
require.Len(t, edition2023.GetMessages(), 3)
msg2023 := edition2023.GetMessage("TestMessage")
require.NotNil(t, msg2023)
require.True(t, msg2023.IsEditions())
require.False(t, msg2023.IsProto3())

// Test edition 2024 messages
require.Len(t, edition2024.GetMessages(), 3)
msg2024 := edition2024.GetMessage("TestMessage")
require.NotNil(t, msg2024)
require.True(t, msg2024.IsEditions())
require.False(t, msg2024.IsProto3())

// Test nested message in edition 2024
nested := msg2024.GetMessage("NestedData")
require.NotNil(t, nested)
require.True(t, nested.IsEditions())
require.False(t, nested.IsProto3())
}

func TestFieldPresenceBehavior(t *testing.T) {
t.Parallel()

set, err := utils.LoadDescriptorSet("fixtures", "fileset.pb")
require.NoError(t, err)

req := utils.CreateGenRequest(set, "todo.proto", "edition2023.proto", "edition2023_implicit.proto")
files := protokit.ParseCodeGenRequest(req)

proto3File := files[0] // todo.proto (proto3)
editionExplicitFile := files[1] // edition2023.proto (explicit field presence)
editionImplicitFile := files[2] // edition2023_implicit.proto (implicit field presence)

// Test proto3 file
require.True(t, proto3File.IsProto3())
require.False(t, proto3File.HasExplicitFieldPresence())
require.Equal(t, "proto3", proto3File.GetSyntax())

// Test editions file with explicit field presence (default for editions)
require.False(t, editionExplicitFile.IsProto3())
require.True(t, editionExplicitFile.HasExplicitFieldPresence())
require.True(t, editionExplicitFile.IsEditions())
require.Equal(t, "editions", editionExplicitFile.GetSyntax())

// Test editions file with implicit field presence (proto3-like semantics)
require.True(t, editionImplicitFile.IsProto3())
require.False(t, editionImplicitFile.HasExplicitFieldPresence())
require.True(t, editionImplicitFile.IsEditions())
require.Equal(t, "editions", editionImplicitFile.GetSyntax())
}
Loading
Loading