From e388356e07fda5ce3e92c2beb3ee7b028d3f49a0 Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Sun, 3 Nov 2024 10:16:10 -0500 Subject: [PATCH 1/3] impl(generator): handle nested messages and enums in Rust The mapping nested messages and enums uses a public module within the crate. That requires a new "snake case" accessor for the message name, as Rust uses snake case for modules. We also need a boolean to find out if the module is needed at all. And we need to get a different attribute for the fully qualified name of each message and enum, as we must refer to the qualified name of the generated struct when using the type. --- generator/internal/genclient/genclient.go | 66 +++- .../language/internal/golang/golang.go | 12 + .../language/internal/golang/golang_test.go | 69 ++++ .../genclient/language/internal/rust/rust.go | 40 ++- .../language/internal/rust/rust_test.go | 67 ++++ generator/internal/genclient/templatedata.go | 21 +- generator/templates/rust/model.mustache | 23 +- .../testdata/rust/gclient/golden/model.rs | 306 ++++++++++-------- .../testdata/rust/openapi/golden/model.rs | 18 +- 9 files changed, 449 insertions(+), 173 deletions(-) diff --git a/generator/internal/genclient/genclient.go b/generator/internal/genclient/genclient.go index 6d01b60b9..3b6418665 100644 --- a/generator/internal/genclient/genclient.go +++ b/generator/internal/genclient/genclient.go @@ -39,12 +39,24 @@ type LanguageCodec interface { // FieldType returns a string representation of a message field type. FieldType(f *Field, state *APIState) string MethodInOutTypeName(id string, state *APIState) string - // MessageName returns a string representation of a message name. + // The (unqualified) message name, as used when defining the type to + // represent it. MessageName(m *Message, state *APIState) string - // EnumName returns a string representation of an enum name. + // The fully-qualified message name, as used when referring to the name from + // another place in the package. + FQMessageName(m *Message, state *APIState) string + // The (unqualified) enum name, as used when defining the type to + // represent it. EnumName(e *Enum, state *APIState) string - // EnumValueName returns a string representation of an enum value name. + // The fully-qualified enum name, as used when referring to the name from + // another place in the package. + FQEnumName(e *Enum, state *APIState) string + // The (unqualified) enum value name, as used when defining the constant, + // variable, or enum value that holds it. EnumValueName(e *EnumValue, state *APIState) string + // The fully qualified enum value name, as used when using the constant, + // variable, or enum value that hodls it. + FQEnumValueName(e *EnumValue, state *APIState) string // BodyAccessor returns a string representation of the accessor used to // get the body out of a request. For instance this might return `.Body()`. BodyAccessor(m *Method, state *APIState) string @@ -132,3 +144,51 @@ func Generate(req *GenerateRequest) (*Output, error) { var output *Output return output, nil } + +// Creates a populated API state from lists of messages, enums, and services. +func NewTestAPI(messages []*Message, enums []*Enum, services []*Service) *API { + state := &APIState{ + MessageByID: make(map[string]*Message), + EnumByID: make(map[string]*Enum), + ServiceByID: make(map[string]*Service), + } + for _, m := range messages { + state.MessageByID[m.ID] = m + } + for _, e := range enums { + state.EnumByID[e.ID] = e + } + for _, s := range services { + state.ServiceByID[s.ID] = s + } + for _, m := range messages { + parentID := parentName(m.ID) + parent := state.MessageByID[parentID] + if parent != nil { + m.Parent = parent + parent.Messages = append(parent.Messages, m) + } + } + for _, e := range enums { + parent := state.MessageByID[parentName(e.ID)] + if parent != nil { + e.Parent = parent + parent.Enums = append(parent.Enums, e) + } + } + + return &API{ + Name: "Test", + Messages: messages, + Enums: enums, + Services: services, + State: state, + } +} + +func parentName(id string) string { + if lastIndex := strings.LastIndex(id, "."); lastIndex != -1 { + return id[:lastIndex] + } + return "." +} diff --git a/generator/internal/genclient/language/internal/golang/golang.go b/generator/internal/genclient/language/internal/golang/golang.go index ed3b11e2c..4ee252b9e 100644 --- a/generator/internal/genclient/language/internal/golang/golang.go +++ b/generator/internal/genclient/language/internal/golang/golang.go @@ -108,6 +108,10 @@ func (c *Codec) MessageName(m *genclient.Message, state *genclient.APIState) str return strcase.ToCamel(m.Name) } +func (c *Codec) FQMessageName(m *genclient.Message, state *genclient.APIState) string { + return c.MessageName(m, state) +} + func (c *Codec) EnumName(e *genclient.Enum, state *genclient.APIState) string { if e.Parent != nil { return c.MessageName(e.Parent, state) + "_" + strcase.ToCamel(e.Name) @@ -115,6 +119,10 @@ func (c *Codec) EnumName(e *genclient.Enum, state *genclient.APIState) string { return strcase.ToCamel(e.Name) } +func (c *Codec) FQEnumName(e *genclient.Enum, state *genclient.APIState) string { + return c.EnumName(e, state) +} + func (c *Codec) EnumValueName(e *genclient.EnumValue, state *genclient.APIState) string { if e.Parent.Parent != nil { return c.MessageName(e.Parent.Parent, state) + "_" + strings.ToUpper(e.Name) @@ -122,6 +130,10 @@ func (c *Codec) EnumValueName(e *genclient.EnumValue, state *genclient.APIState) return strings.ToUpper(e.Name) } +func (c *Codec) FQEnumValueName(v *genclient.EnumValue, state *genclient.APIState) string { + return c.EnumValueName(v, state) +} + func (c *Codec) BodyAccessor(m *genclient.Method, state *genclient.APIState) string { if m.HTTPInfo.Body == "*" { // no accessor needed, use the whole request diff --git a/generator/internal/genclient/language/internal/golang/golang_test.go b/generator/internal/genclient/language/internal/golang/golang_test.go index 50e7186f3..1826af5cd 100644 --- a/generator/internal/genclient/language/internal/golang/golang_test.go +++ b/generator/internal/genclient/language/internal/golang/golang_test.go @@ -16,6 +16,8 @@ package golang import ( "testing" + + "github.com/googleapis/google-cloud-rust/generator/internal/genclient" ) type CaseConvertTest struct { @@ -53,3 +55,70 @@ func TestToPascal(t *testing.T) { } } } + +func TestMessageNames(t *testing.T) { + message := &genclient.Message{ + Name: "Replication", + ID: "..Replication", + Fields: []*genclient.Field{ + { + Name: "automatic", + Typez: genclient.MESSAGE_TYPE, + TypezID: "..Automatic", + Optional: true, + Repeated: false, + }, + }, + } + nested := &genclient.Message{ + Name: "Automatic", + ID: "..Replication.Automatic", + } + + api := genclient.NewTestAPI([]*genclient.Message{message, nested}, []*genclient.Enum{}, []*genclient.Service{}) + + c := &Codec{} + if got := c.MessageName(message, api.State); got != "Replication" { + t.Errorf("mismatched message name, want=Replication, got=%s", got) + } + if got := c.FQMessageName(message, api.State); got != "Replication" { + t.Errorf("mismatched message name, want=Replication, got=%s", got) + } + + if got := c.MessageName(nested, api.State); got != "Replication_Automatic" { + t.Errorf("mismatched message name, want=SecretVersion_Automatic, got=%s", got) + } + if got := c.FQMessageName(nested, api.State); got != "Replication_Automatic" { + t.Errorf("mismatched message name, want=Replication_Automatic, got=%s", got) + } +} + +func TestEnumNames(t *testing.T) { + message := &genclient.Message{ + Name: "SecretVersion", + ID: "..SecretVersion", + Fields: []*genclient.Field{ + { + Name: "automatic", + Typez: genclient.MESSAGE_TYPE, + TypezID: "..Automatic", + Optional: true, + Repeated: false, + }, + }, + } + nested := &genclient.Enum{ + Name: "State", + ID: "..SecretVersion.State", + } + + api := genclient.NewTestAPI([]*genclient.Message{message}, []*genclient.Enum{nested}, []*genclient.Service{}) + + c := &Codec{} + if got := c.EnumName(nested, api.State); got != "SecretVersion_State" { + t.Errorf("mismatched message name, want=SecretVersion_Automatic, got=%s", got) + } + if got := c.FQEnumName(nested, api.State); got != "SecretVersion_State" { + t.Errorf("mismatched message name, want=SecretVersion_State, got=%s", got) + } +} diff --git a/generator/internal/genclient/language/internal/rust/rust.go b/generator/internal/genclient/language/internal/rust/rust.go index 8d971a8c5..b1ca98402 100644 --- a/generator/internal/genclient/language/internal/rust/rust.go +++ b/generator/internal/genclient/language/internal/rust/rust.go @@ -30,6 +30,7 @@ func NewCodec() *Codec { type Codec struct{} func (c *Codec) LoadWellKnownTypes(s *genclient.APIState) { + // TODO(#77) - replace these placeholders with real types timestamp := &genclient.Message{ ID: ".google.protobuf.Timestamp", Name: "String", @@ -98,14 +99,17 @@ func (c *Codec) FieldType(f *genclient.Field, state *genclient.APIState) string val := c.FieldType(m.Fields[1], state) return "Option>" } - return "Option<" + c.MessageName(m, state) + ">" + if strings.HasPrefix(m.ID, ".google.protobuf.") { + return "Option /* TODO(#77) - handle " + f.TypezID + " */" + } + return "Option<" + c.FQMessageName(m, state) + ">" } else if f.Typez == genclient.ENUM_TYPE { e, ok := state.EnumByID[f.TypezID] if !ok { slog.Error("unable to lookup type", "id", f.TypezID) return "" } - return c.EnumName(e, state) + return c.FQEnumName(e, state) } else if f.Typez == genclient.GROUP_TYPE { slog.Error("TODO(#39) - better handling of `oneof` fields") return "" @@ -130,22 +134,35 @@ func (c *Codec) MethodInOutTypeName(id string, s *genclient.APIState) string { } func (c *Codec) MessageName(m *genclient.Message, state *genclient.APIState) string { - if m.Parent != nil { - return c.MessageName(m.Parent, state) + "_" + strcase.ToCamel(m.Name) - } if m.Package != "" { - return m.Package + "." + strcase.ToCamel(m.Name) + return m.Package + "." + c.ToPascal(m.Name) } return c.ToPascal(m.Name) } -func (c *Codec) EnumName(e *genclient.Enum, state *genclient.APIState) string { - if e.Parent != nil { - return c.MessageName(e.Parent, state) + "_" + strcase.ToCamel(e.Name) +func (c *Codec) messageScopeName(m *genclient.Message) string { + if m == nil { + return "crate" } + return c.messageScopeName(m.Parent) + "::" + c.ToSnake(m.Name) +} + +func (c *Codec) enumScopeName(e *genclient.Enum) string { + return c.messageScopeName(e.Parent) +} + +func (c *Codec) FQMessageName(m *genclient.Message, _ *genclient.APIState) string { + return c.messageScopeName(m.Parent) + "::" + c.ToPascal(m.Name) +} + +func (c *Codec) EnumName(e *genclient.Enum, state *genclient.APIState) string { return c.ToPascal(e.Name) } +func (c *Codec) FQEnumName(e *genclient.Enum, _ *genclient.APIState) string { + return c.messageScopeName(e.Parent) + "::" + c.ToPascal(e.Name) +} + func (c *Codec) EnumValueName(e *genclient.EnumValue, state *genclient.APIState) string { if e.Parent.Parent != nil { return c.MessageName(e.Parent.Parent, state) + "_" + strcase.ToCamel(e.Name) @@ -153,6 +170,11 @@ func (c *Codec) EnumValueName(e *genclient.EnumValue, state *genclient.APIState) return c.ToPascal(e.Name) } +func (c *Codec) FQEnumValueName(v *genclient.EnumValue, _ *genclient.APIState) string { + // TODO(#76) - these will be `const` strings and therefore should be SNAKE_UPPERCASE. + return c.enumScopeName(v.Parent) + "::" + c.ToSnake(v.Name) +} + func (c *Codec) BodyAccessor(m *genclient.Method, state *genclient.APIState) string { if m.HTTPInfo.Body == "*" { // no accessor needed, use the whole request diff --git a/generator/internal/genclient/language/internal/rust/rust_test.go b/generator/internal/genclient/language/internal/rust/rust_test.go index 7aea6637c..b8d907f48 100644 --- a/generator/internal/genclient/language/internal/rust/rust_test.go +++ b/generator/internal/genclient/language/internal/rust/rust_test.go @@ -86,3 +86,70 @@ func TestToPascal(t *testing.T) { } } } + +func TestMessageNames(t *testing.T) { + message := &genclient.Message{ + Name: "Replication", + ID: "..Replication", + Fields: []*genclient.Field{ + { + Name: "automatic", + Typez: genclient.MESSAGE_TYPE, + TypezID: "..Automatic", + Optional: true, + Repeated: false, + }, + }, + } + nested := &genclient.Message{ + Name: "Automatic", + ID: "..Replication.Automatic", + } + + api := genclient.NewTestAPI([]*genclient.Message{message, nested}, []*genclient.Enum{}, []*genclient.Service{}) + + c := &Codec{} + if got := c.MessageName(message, api.State); got != "Replication" { + t.Errorf("mismatched message name, want=Replication, got=%s", got) + } + if got := c.FQMessageName(message, api.State); got != "crate::Replication" { + t.Errorf("mismatched message name, want=crate::Replication, got=%s", got) + } + + if got := c.MessageName(nested, api.State); got != "Automatic" { + t.Errorf("mismatched message name, want=Automatic, got=%s", got) + } + if got := c.FQMessageName(nested, api.State); got != "crate::replication::Automatic" { + t.Errorf("mismatched message name, want=crate::replication::Automatic, got=%s", got) + } +} + +func TestEnumNames(t *testing.T) { + message := &genclient.Message{ + Name: "SecretVersion", + ID: "..SecretVersion", + Fields: []*genclient.Field{ + { + Name: "automatic", + Typez: genclient.MESSAGE_TYPE, + TypezID: "..Automatic", + Optional: true, + Repeated: false, + }, + }, + } + nested := &genclient.Enum{ + Name: "State", + ID: "..SecretVersion.State", + } + + api := genclient.NewTestAPI([]*genclient.Message{message}, []*genclient.Enum{nested}, []*genclient.Service{}) + + c := &Codec{} + if got := c.EnumName(nested, api.State); got != "State" { + t.Errorf("mismatched message name, want=Automatic, got=%s", got) + } + if got := c.FQEnumName(nested, api.State); got != "crate::secret_version::State" { + t.Errorf("mismatched message name, want=crate::secret_version::State, got=%s", got) + } +} diff --git a/generator/internal/genclient/templatedata.go b/generator/internal/genclient/templatedata.go index d5df31aee..21ffe6241 100644 --- a/generator/internal/genclient/templatedata.go +++ b/generator/internal/genclient/templatedata.go @@ -202,11 +202,30 @@ func (m *message) Enums() []*enum { }) } -// NameToCamel converts a Name to CamelCase. func (m *message) Name() string { return m.c.MessageName(m.s, m.state) } +func (m *message) QualifiedName() string { + return m.c.FQMessageName(m.s, m.state) +} + +func (m *message) NameSnakeCase() string { + return m.c.ToSnake(m.s.Name) +} + +func (m *message) HasNestedTypes() bool { + if len(m.s.Enums) > 0 { + return true + } + for _, child := range m.s.Messages { + if !child.IsMap { + return true + } + } + return false +} + func (m *message) DocLines() []string { // TODO(codyoss): https://github.com/googleapis/google-cloud-rust/issues/33 ss := strings.Split(m.s.Documentation, "\n") diff --git a/generator/templates/rust/model.mustache b/generator/templates/rust/model.mustache index d59ee0438..21be65d8b 100644 --- a/generator/templates/rust/model.mustache +++ b/generator/templates/rust/model.mustache @@ -15,11 +15,18 @@ pub struct {{Name}} { pub {{NameToSnake}}: {{{FieldType}}}, {{/Fields}} } -{{#NestedMessages}} -{{! TODO(codyoss) https://github.com/googleapis/google-cloud-rust/issues/40 }} -{{> model}} -{{/NestedMessages}} -{{#Enums}} -{{> enum}} -{{/Enums}} -{{/IsMap}} \ No newline at end of file +{{#HasNestedTypes}} + +/// Defines additional types related to {{Name}} +pub mod {{NameSnakeCase}} { +{{/HasNestedTypes}} + {{#NestedMessages}} + {{> model}} + {{/NestedMessages}} + {{#Enums}} + {{> enum}} + {{/Enums}} +{{#HasNestedTypes}} +} +{{/HasNestedTypes}} +{{/IsMap}} diff --git a/generator/testdata/rust/gclient/golden/model.rs b/generator/testdata/rust/gclient/golden/model.rs index 4f86ab4bf..b50336f60 100644 --- a/generator/testdata/rust/gclient/golden/model.rs +++ b/generator/testdata/rust/gclient/golden/model.rs @@ -23,11 +23,11 @@ pub struct Secret { /// the [Secret][google.cloud.secretmanager.v1.Secret]. /// /// The replication policy cannot be changed after the Secret has been created. - pub replication: Option, + pub replication: Option, /// Output only. The time at which the /// [Secret][google.cloud.secretmanager.v1.Secret] was created. - pub create_time: Option, + pub create_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// The labels assigned to this Secret. /// @@ -44,16 +44,16 @@ pub struct Secret { /// Optional. A list of up to 10 Pub/Sub topics to which messages are published /// when control plane operations are called on the secret or its versions. - pub topics: Option, + pub topics: Option, /// Optional. Timestamp in UTC when the /// [Secret][google.cloud.secretmanager.v1.Secret] is scheduled to expire. /// This is always provided on output, regardless of what was sent on input. - pub expire_time: Option, + pub expire_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Input only. The TTL for the /// [Secret][google.cloud.secretmanager.v1.Secret]. - pub ttl: Option, + pub ttl: Option /* TODO(#77) - handle .google.protobuf.Duration */, /// Optional. Etag of the currently stored /// [Secret][google.cloud.secretmanager.v1.Secret]. @@ -62,7 +62,7 @@ pub struct Secret { /// Optional. Rotation policy attached to the /// [Secret][google.cloud.secretmanager.v1.Secret]. May be excluded if there is /// no rotation policy. - pub rotation: Option, + pub rotation: Option, /// Optional. Mapping from version alias to version name. /// @@ -97,7 +97,7 @@ pub struct Secret { /// For secret with TTL>0, version destruction doesn't happen immediately /// on calling destroy instead the version goes to a disabled state and /// destruction happens after the TTL expires. - pub version_destroy_ttl: Option, + pub version_destroy_ttl: Option /* TODO(#77) - handle .google.protobuf.Duration */, /// Optional. The customer-managed encryption configuration of the Regionalised /// Secrets. If no configuration is provided, Google-managed default encryption @@ -108,7 +108,7 @@ pub struct Secret { /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion] added /// afterwards. They do not apply retroactively to existing /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion]. - pub customer_managed_encryption: Option, + pub customer_managed_encryption: Option, } /// A secret version resource in the Secret Manager API. @@ -128,22 +128,22 @@ pub struct SecretVersion { /// Output only. The time at which the /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] was created. - pub create_time: Option, + pub create_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Output only. The time this /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] was destroyed. /// Only present if [state][google.cloud.secretmanager.v1.SecretVersion.state] /// is /// [DESTROYED][google.cloud.secretmanager.v1.SecretVersion.State.DESTROYED]. - pub destroy_time: Option, + pub destroy_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Output only. The current state of the /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. - pub state: SecretVersion_State, + pub state: crate::secret_version::State, /// The replication status of the /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. - pub replication_status: Option, + pub replication_status: Option, /// Output only. Etag of the currently stored /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. @@ -163,39 +163,43 @@ pub struct SecretVersion { /// destroyed, the version is moved to disabled state and it is scheduled for /// destruction. The version is destroyed only after the /// `scheduled_destroy_time`. - pub scheduled_destroy_time: Option, + pub scheduled_destroy_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Output only. The customer-managed encryption status of the /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. Only /// populated if customer-managed encryption is used and /// [Secret][google.cloud.secretmanager.v1.Secret] is a Regionalised Secret. - pub customer_managed_encryption: Option, + pub customer_managed_encryption: Option, } -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct SecretVersion_State(i32); +/// Defines additional types related to SecretVersion +pub mod secret_version { -impl SecretVersion_State { + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + pub struct State(i32); - // Not specified. This value is unused and invalid. - pub const SecretVersion_StateUnspecified: SecretVersion_State = SecretVersion_State(0); + impl State { - // The [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] may be - // accessed. - pub const SecretVersion_Enabled: SecretVersion_State = SecretVersion_State(1); + // Not specified. This value is unused and invalid. + pub const SecretVersion_StateUnspecified: State = State(0); - // The [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] may not - // be accessed, but the secret data is still available and can be placed - // back into the - // [ENABLED][google.cloud.secretmanager.v1.SecretVersion.State.ENABLED] - // state. - pub const SecretVersion_Disabled: SecretVersion_State = SecretVersion_State(2); + // The [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] may be + // accessed. + pub const SecretVersion_Enabled: State = State(1); + + // The [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] may not + // be accessed, but the secret data is still available and can be placed + // back into the + // [ENABLED][google.cloud.secretmanager.v1.SecretVersion.State.ENABLED] + // state. + pub const SecretVersion_Disabled: State = State(2); + + // The [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] is + // destroyed and the secret data is no longer stored. A version may not + // leave this state once entered. + pub const SecretVersion_Destroyed: State = State(3); + }} - // The [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] is - // destroyed and the secret data is no longer stored. A version may not - // leave this state once entered. - pub const SecretVersion_Destroyed: SecretVersion_State = SecretVersion_State(3); -} /// A policy that defines the replication and encryption configuration of data. #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] @@ -204,69 +208,77 @@ pub struct Replication { /// The [Secret][google.cloud.secretmanager.v1.Secret] will automatically be /// replicated without any restrictions. - pub automatic: Option, + pub automatic: Option, /// The [Secret][google.cloud.secretmanager.v1.Secret] will only be /// replicated into the locations specified. - pub user_managed: Option, + pub user_managed: Option, } -/// A replication policy that replicates the -/// [Secret][google.cloud.secretmanager.v1.Secret] payload without any -/// restrictions. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct Replication_Automatic { - - /// Optional. The customer-managed encryption configuration of the - /// [Secret][google.cloud.secretmanager.v1.Secret]. If no configuration is - /// provided, Google-managed default encryption is used. - /// - /// Updates to the [Secret][google.cloud.secretmanager.v1.Secret] encryption - /// configuration only apply to - /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion] added - /// afterwards. They do not apply retroactively to existing - /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion]. - pub customer_managed_encryption: Option, -} - -/// A replication policy that replicates the -/// [Secret][google.cloud.secretmanager.v1.Secret] payload into the locations -/// specified in [Secret.replication.user_managed.replicas][] -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct Replication_UserManaged { - - /// Required. The list of Replicas for this - /// [Secret][google.cloud.secretmanager.v1.Secret]. - /// - /// Cannot be empty. - pub replicas: Option, -} - -/// Represents a Replica for this -/// [Secret][google.cloud.secretmanager.v1.Secret]. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct Replication_UserManaged_Replica { - - /// The canonical IDs of the location to replicate data. - /// For example: `"us-east1"`. - pub location: String, - - /// Optional. The customer-managed encryption configuration of the - /// [User-Managed Replica][Replication.UserManaged.Replica]. If no - /// configuration is provided, Google-managed default encryption is used. - /// - /// Updates to the [Secret][google.cloud.secretmanager.v1.Secret] - /// encryption configuration only apply to - /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion] added - /// afterwards. They do not apply retroactively to existing - /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion]. - pub customer_managed_encryption: Option, +/// Defines additional types related to Replication +pub mod replication { + + /// A replication policy that replicates the + /// [Secret][google.cloud.secretmanager.v1.Secret] payload without any + /// restrictions. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[serde(rename_all = "camelCase")] + #[non_exhaustive] + pub struct Automatic { + + /// Optional. The customer-managed encryption configuration of the + /// [Secret][google.cloud.secretmanager.v1.Secret]. If no configuration is + /// provided, Google-managed default encryption is used. + /// + /// Updates to the [Secret][google.cloud.secretmanager.v1.Secret] encryption + /// configuration only apply to + /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion] added + /// afterwards. They do not apply retroactively to existing + /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion]. + pub customer_managed_encryption: Option, + } + + /// A replication policy that replicates the + /// [Secret][google.cloud.secretmanager.v1.Secret] payload into the locations + /// specified in [Secret.replication.user_managed.replicas][] + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[serde(rename_all = "camelCase")] + #[non_exhaustive] + pub struct UserManaged { + + /// Required. The list of Replicas for this + /// [Secret][google.cloud.secretmanager.v1.Secret]. + /// + /// Cannot be empty. + pub replicas: Option, + } + + /// Defines additional types related to UserManaged + pub mod user_managed { + + /// Represents a Replica for this + /// [Secret][google.cloud.secretmanager.v1.Secret]. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[serde(rename_all = "camelCase")] + #[non_exhaustive] + pub struct Replica { + + /// The canonical IDs of the location to replicate data. + /// For example: `"us-east1"`. + pub location: String, + + /// Optional. The customer-managed encryption configuration of the + /// [User-Managed Replica][Replication.UserManaged.Replica]. If no + /// configuration is provided, Google-managed default encryption is used. + /// + /// Updates to the [Secret][google.cloud.secretmanager.v1.Secret] + /// encryption configuration only apply to + /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion] added + /// afterwards. They do not apply retroactively to existing + /// [SecretVersions][google.cloud.secretmanager.v1.SecretVersion]. + pub customer_managed_encryption: Option, + } + } } /// Configuration for encrypting secret payloads using customer-managed @@ -306,7 +318,7 @@ pub struct ReplicationStatus { /// Only populated if the parent /// [Secret][google.cloud.secretmanager.v1.Secret] has an automatic /// replication policy. - pub automatic: Option, + pub automatic: Option, /// Describes the replication status of a /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] with @@ -315,57 +327,65 @@ pub struct ReplicationStatus { /// Only populated if the parent /// [Secret][google.cloud.secretmanager.v1.Secret] has a user-managed /// replication policy. - pub user_managed: Option, -} - -/// The replication status of a -/// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] using -/// automatic replication. -/// -/// Only populated if the parent [Secret][google.cloud.secretmanager.v1.Secret] -/// has an automatic replication policy. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct ReplicationStatus_AutomaticStatus { - - /// Output only. The customer-managed encryption status of the - /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. Only - /// populated if customer-managed encryption is used. - pub customer_managed_encryption: Option, -} - -/// The replication status of a -/// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] using -/// user-managed replication. -/// -/// Only populated if the parent [Secret][google.cloud.secretmanager.v1.Secret] -/// has a user-managed replication policy. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct ReplicationStatus_UserManagedStatus { - - /// Output only. The list of replica statuses for the - /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. - pub replicas: Option, + pub user_managed: Option, } -/// Describes the status of a user-managed replica for the -/// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct ReplicationStatus_UserManagedStatus_ReplicaStatus { +/// Defines additional types related to ReplicationStatus +pub mod replication_status { - /// Output only. The canonical ID of the replica location. - /// For example: `"us-east1"`. - pub location: String, - - /// Output only. The customer-managed encryption status of the - /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. Only - /// populated if customer-managed encryption is used. - pub customer_managed_encryption: Option, + /// The replication status of a + /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] using + /// automatic replication. + /// + /// Only populated if the parent [Secret][google.cloud.secretmanager.v1.Secret] + /// has an automatic replication policy. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[serde(rename_all = "camelCase")] + #[non_exhaustive] + pub struct AutomaticStatus { + + /// Output only. The customer-managed encryption status of the + /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. Only + /// populated if customer-managed encryption is used. + pub customer_managed_encryption: Option, + } + + /// The replication status of a + /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion] using + /// user-managed replication. + /// + /// Only populated if the parent [Secret][google.cloud.secretmanager.v1.Secret] + /// has a user-managed replication policy. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[serde(rename_all = "camelCase")] + #[non_exhaustive] + pub struct UserManagedStatus { + + /// Output only. The list of replica statuses for the + /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. + pub replicas: Option, + } + + /// Defines additional types related to UserManagedStatus + pub mod user_managed_status { + + /// Describes the status of a user-managed replica for the + /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[serde(rename_all = "camelCase")] + #[non_exhaustive] + pub struct ReplicaStatus { + + /// Output only. The canonical ID of the replica location. + /// For example: `"us-east1"`. + pub location: String, + + /// Output only. The customer-managed encryption status of the + /// [SecretVersion][google.cloud.secretmanager.v1.SecretVersion]. Only + /// populated if customer-managed encryption is used. + pub customer_managed_encryption: Option, + } + } } /// Describes the status of customer-managed encryption. @@ -414,7 +434,7 @@ pub struct Rotation { /// MUST be set if /// [rotation_period][google.cloud.secretmanager.v1.Rotation.rotation_period] /// is set. - pub next_rotation_time: Option, + pub next_rotation_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Input only. The Duration between rotation notifications. Must be in seconds /// and at least 3600s (1h) and at most 3153600000s (100 years). @@ -427,7 +447,7 @@ pub struct Rotation { /// [next_rotation_time][google.cloud.secretmanager.v1.Rotation.next_rotation_time] /// will be advanced by this period when the service automatically sends /// rotation notifications. - pub rotation_period: Option, + pub rotation_period: Option /* TODO(#77) - handle .google.protobuf.Duration */, } /// A secret payload resource in the Secret Manager API. This contains the @@ -481,7 +501,7 @@ pub struct CreateSecretRequest { /// Required. A [Secret][google.cloud.secretmanager.v1.Secret] with initial /// field values. - pub secret: Option, + pub secret: Option, } /// Request message for diff --git a/generator/testdata/rust/openapi/golden/model.rs b/generator/testdata/rust/openapi/golden/model.rs index 706665a1b..a5600ee63 100755 --- a/generator/testdata/rust/openapi/golden/model.rs +++ b/generator/testdata/rust/openapi/golden/model.rs @@ -81,7 +81,7 @@ pub struct Secret { pub replication: Option, /// Output only. The time at which the Secret was created. - pub create_time: Option, + pub create_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// The labels assigned to this Secret. /// @@ -102,10 +102,10 @@ pub struct Secret { /// Optional. Timestamp in UTC when the Secret is scheduled to expire. This is /// always provided on output, regardless of what was sent on input. - pub expire_time: Option, + pub expire_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Input only. The TTL for the Secret. - pub ttl: Option, + pub ttl: Option /* TODO(#77) - handle .google.protobuf.Duration */, /// Optional. Etag of the currently stored Secret. pub etag: Option, @@ -147,7 +147,7 @@ pub struct Secret { /// For secret with TTL>0, version destruction doesn't happen immediately /// on calling destroy instead the version goes to a disabled state and /// destruction happens after the TTL expires. - pub version_destroy_ttl: Option, + pub version_destroy_ttl: Option /* TODO(#77) - handle .google.protobuf.Duration */, /// Optional. The customer-managed encryption configuration of the Regionalised Secrets. /// If no configuration is provided, Google-managed default encryption is used. @@ -269,7 +269,7 @@ pub struct Rotation { /// years). /// /// next_rotation_time MUST be set if rotation_period is set. - pub next_rotation_time: Option, + pub next_rotation_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Input only. The Duration between rotation notifications. Must be in seconds /// and at least 3600s (1h) and at most 3153600000s (100 years). @@ -277,7 +277,7 @@ pub struct Rotation { /// If rotation_period is set, next_rotation_time must be set. /// next_rotation_time will be advanced by this period when the service /// automatically sends rotation notifications. - pub rotation_period: Option, + pub rotation_period: Option /* TODO(#77) - handle .google.protobuf.Duration */, } /// Request message for SecretManagerService.AddSecretVersion. @@ -327,12 +327,12 @@ pub struct SecretVersion { pub name: Option, /// Output only. The time at which the SecretVersion was created. - pub create_time: Option, + pub create_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Output only. The time this SecretVersion was destroyed. /// Only present if state is /// DESTROYED. - pub destroy_time: Option, + pub destroy_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Output only. The current state of the SecretVersion. pub state: Option, @@ -353,7 +353,7 @@ pub struct SecretVersion { /// Secret with a valid version destroy TTL, when a secert version is /// destroyed, version is moved to disabled state and it is scheduled for /// destruction Version is destroyed only after the scheduled_destroy_time. - pub scheduled_destroy_time: Option, + pub scheduled_destroy_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, /// Output only. The customer-managed encryption status of the SecretVersion. Only /// populated if customer-managed encryption is used and Secret is From a23054a98b71bd89f03bfb5b56afe984277cafcb Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Mon, 4 Nov 2024 10:17:03 -0500 Subject: [PATCH 2/3] Update golden files after rebase --- .../testdata/rust/openapi/golden/model.rs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/generator/testdata/rust/openapi/golden/model.rs b/generator/testdata/rust/openapi/golden/model.rs index a5600ee63..1fbebcade 100755 --- a/generator/testdata/rust/openapi/golden/model.rs +++ b/generator/testdata/rust/openapi/golden/model.rs @@ -78,7 +78,7 @@ pub struct Secret { /// Optional. Immutable. The replication policy of the secret data attached to the Secret. /// /// The replication policy cannot be changed after the Secret has been created. - pub replication: Option, + pub replication: Option, /// Output only. The time at which the Secret was created. pub create_time: Option /* TODO(#77) - handle .google.protobuf.Timestamp */, @@ -112,7 +112,7 @@ pub struct Secret { /// Optional. Rotation policy attached to the Secret. May be excluded if there is no /// rotation policy. - pub rotation: Option, + pub rotation: Option, /// Optional. Mapping from version alias to version name. /// @@ -155,7 +155,7 @@ pub struct Secret { /// Updates to the Secret encryption configuration only apply to /// SecretVersions added afterwards. They do not apply /// retroactively to existing SecretVersions. - pub customer_managed_encryption: Option, + pub customer_managed_encryption: Option, } /// A policy that defines the replication and encryption configuration of data. @@ -165,10 +165,10 @@ pub struct Secret { pub struct Replication { /// The Secret will automatically be replicated without any restrictions. - pub automatic: Option, + pub automatic: Option, /// The Secret will only be replicated into the locations specified. - pub user_managed: Option, + pub user_managed: Option, } /// A replication policy that replicates the Secret payload without any @@ -184,7 +184,7 @@ pub struct Automatic { /// Updates to the Secret encryption configuration only apply to /// SecretVersions added afterwards. They do not apply /// retroactively to existing SecretVersions. - pub customer_managed_encryption: Option, + pub customer_managed_encryption: Option, } /// Configuration for encrypting secret payloads using customer-managed @@ -238,7 +238,7 @@ pub struct Replica { /// Updates to the Secret encryption configuration only apply to /// SecretVersions added afterwards. They do not apply /// retroactively to existing SecretVersions. - pub customer_managed_encryption: Option, + pub customer_managed_encryption: Option, } /// A Pub/Sub topic which Secret Manager will publish to when control plane @@ -287,7 +287,7 @@ pub struct Rotation { pub struct AddSecretVersionRequest { /// Required. The secret payload of the SecretVersion. - pub payload: Option, + pub payload: Option, } /// A secret payload resource in the Secret Manager API. This contains the @@ -338,7 +338,7 @@ pub struct SecretVersion { pub state: Option, /// The replication status of the SecretVersion. - pub replication_status: Option, + pub replication_status: Option, /// Output only. Etag of the currently stored SecretVersion. pub etag: Option, @@ -358,7 +358,7 @@ pub struct SecretVersion { /// Output only. The customer-managed encryption status of the SecretVersion. Only /// populated if customer-managed encryption is used and Secret is /// a Regionalised Secret. - pub customer_managed_encryption: Option, + pub customer_managed_encryption: Option, } /// The replication status of a SecretVersion. @@ -372,14 +372,14 @@ pub struct ReplicationStatus { /// /// Only populated if the parent Secret has an automatic replication /// policy. - pub automatic: Option, + pub automatic: Option, /// Describes the replication status of a SecretVersion with /// user-managed replication. /// /// Only populated if the parent Secret has a user-managed replication /// policy. - pub user_managed: Option, + pub user_managed: Option, } /// The replication status of a SecretVersion using automatic replication. @@ -393,7 +393,7 @@ pub struct AutomaticStatus { /// Output only. The customer-managed encryption status of the SecretVersion. Only /// populated if customer-managed encryption is used. - pub customer_managed_encryption: Option, + pub customer_managed_encryption: Option, } /// Describes the status of customer-managed encryption. @@ -434,7 +434,7 @@ pub struct ReplicaStatus { /// Output only. The customer-managed encryption status of the SecretVersion. Only /// populated if customer-managed encryption is used. - pub customer_managed_encryption: Option, + pub customer_managed_encryption: Option, } /// A generic empty message that you can re-use to avoid defining duplicated @@ -481,7 +481,7 @@ pub struct AccessSecretVersionResponse { pub name: Option, /// Secret payload - pub payload: Option, + pub payload: Option, } /// Request message for SecretManagerService.DisableSecretVersion. @@ -530,7 +530,7 @@ pub struct SetIamPolicyRequest { /// the policy is limited to a few 10s of KB. An empty policy is a /// valid policy but certain Google Cloud services (such as Projects) /// might reject them. - pub policy: Option, + pub policy: Option, /// OPTIONAL: A FieldMask specifying which fields of the policy to modify. Only /// the fields in the mask will be modified. If no mask is provided, the @@ -783,7 +783,7 @@ pub struct Binding { /// To learn which resources support conditions in their IAM policies, see the /// [IAM /// documentation](https://cloud.google.com/iam/help/conditions/resource-policies). - pub condition: Option, + pub condition: Option, } /// Represents a textual expression in the Common Expression Language (CEL) From 626ad8750509dbff5d5ba03b6b1276bbcecc52f9 Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Mon, 4 Nov 2024 10:38:47 -0500 Subject: [PATCH 3/3] Address review comments --- .../genclient/language/internal/rust/rust_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/generator/internal/genclient/language/internal/rust/rust_test.go b/generator/internal/genclient/language/internal/rust/rust_test.go index b8d907f48..b2059c281 100644 --- a/generator/internal/genclient/language/internal/rust/rust_test.go +++ b/generator/internal/genclient/language/internal/rust/rust_test.go @@ -110,17 +110,17 @@ func TestMessageNames(t *testing.T) { c := &Codec{} if got := c.MessageName(message, api.State); got != "Replication" { - t.Errorf("mismatched message name, want=Replication, got=%s", got) + t.Errorf("mismatched message name, got=%s, want=Replication", got) } if got := c.FQMessageName(message, api.State); got != "crate::Replication" { - t.Errorf("mismatched message name, want=crate::Replication, got=%s", got) + t.Errorf("mismatched message name, got=%s, want=crate::Replication", got) } if got := c.MessageName(nested, api.State); got != "Automatic" { - t.Errorf("mismatched message name, want=Automatic, got=%s", got) + t.Errorf("mismatched message name, got=%s, want=Automatic", got) } if got := c.FQMessageName(nested, api.State); got != "crate::replication::Automatic" { - t.Errorf("mismatched message name, want=crate::replication::Automatic, got=%s", got) + t.Errorf("mismatched message name, got=%s, want=crate::replication::Automatic", got) } } @@ -147,9 +147,9 @@ func TestEnumNames(t *testing.T) { c := &Codec{} if got := c.EnumName(nested, api.State); got != "State" { - t.Errorf("mismatched message name, want=Automatic, got=%s", got) + t.Errorf("mismatched message name, got=%s, want=Automatic", got) } if got := c.FQEnumName(nested, api.State); got != "crate::secret_version::State" { - t.Errorf("mismatched message name, want=crate::secret_version::State, got=%s", got) + t.Errorf("mismatched message name, got=%s, want=crate::secret_version::State", got) } }