diff --git a/refs/convert.go b/refs/convert.go index 8b89b71..80b03d2 100644 --- a/refs/convert.go +++ b/refs/convert.go @@ -276,3 +276,31 @@ func (s *Signature) FromGRPCMessage(m grpc.Message) error { return nil } + +// ToGRPCMessage forms refs.SubnetID message and returns it as grpc.Message. +func (s *SubnetID) ToGRPCMessage() grpc.Message { + var m *refs.SubnetID + + if s != nil { + m = new(refs.SubnetID) + + m.SetValue(s.value) + } + + return m +} + +// FromGRPCMessage restores Info from grpc.Message. +// +// Supported types: +// * refs.SubnetID. +func (s *SubnetID) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*refs.SubnetID) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + s.value = v.GetValue() + + return nil +} diff --git a/refs/grpc/types.go b/refs/grpc/types.go index 22448e5..6d86824 100644 --- a/refs/grpc/types.go +++ b/refs/grpc/types.go @@ -89,3 +89,10 @@ func (x *ChecksumType) FromString(s string) bool { return ok } + +// SetValue sets subnet identifier in a base-10 integer format. +func (x *SubnetID) SetValue(v uint32) { + if x != nil { + x.Value = v + } +} diff --git a/refs/marshal.go b/refs/marshal.go index e8d2565..11feb22 100644 --- a/refs/marshal.go +++ b/refs/marshal.go @@ -316,3 +316,35 @@ func (v *Version) StableSize() (size int) { func (v *Version) Unmarshal(data []byte) error { return message.Unmarshal(v, data, new(refs.Version)) } + +// SubnetID message field numbers +const ( + _ = iota + subnetIDValFNum +) + +// StableMarshal marshals SubnetID to NeoFS API V2 binary format (Protocol Buffers with direct field order). +// +// Returns a slice of recorded data. Data is written to the provided buffer if there is enough space. +func (s *SubnetID) StableMarshal(buf []byte) ([]byte, error) { + if s == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, s.StableSize()) + } + + proto.Fixed32Marshal(subnetIDValFNum, buf, s.value) + + return buf, nil +} + +// StableSize returns the number of bytes required to write SubnetID in NeoFS API V2 binary format (see StableMarshal). +func (s *SubnetID) StableSize() (size int) { + if s != nil { + size += proto.Fixed32Size(subnetIDValFNum, s.value) + } + + return +} diff --git a/refs/message_test.go b/refs/message_test.go index d5e591f..f54b2b5 100644 --- a/refs/message_test.go +++ b/refs/message_test.go @@ -17,5 +17,6 @@ func TestMessageConvert(t *testing.T) { func(empty bool) message.Message { return refstest.GenerateChecksum(empty) }, func(empty bool) message.Message { return refstest.GenerateSignature(empty) }, func(empty bool) message.Message { return refstest.GenerateVersion(empty) }, + func(empty bool) message.Message { return refstest.GenerateSubnetID(empty) }, ) } diff --git a/refs/test/generate.go b/refs/test/generate.go index e8a5e6e..835f358 100644 --- a/refs/test/generate.go +++ b/refs/test/generate.go @@ -103,3 +103,13 @@ func GenerateChecksum(empty bool) *refs.Checksum { return m } + +func GenerateSubnetID(empty bool) *refs.SubnetID { + m := new(refs.SubnetID) + + if !empty { + m.SetValue(666) + } + + return m +} diff --git a/subnet/encoding_test.go b/subnet/encoding_test.go new file mode 100644 index 0000000..e85c02b --- /dev/null +++ b/subnet/encoding_test.go @@ -0,0 +1,15 @@ +package subnet_test + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/rpc/message" + messagetest "github.com/nspcc-dev/neofs-api-go/v2/rpc/message/test" + subnettest "github.com/nspcc-dev/neofs-api-go/v2/subnet/test" +) + +func TestMessageConvert(t *testing.T) { + messagetest.TestRPCMessage(t, + func(empty bool) message.Message { return subnettest.GenerateSubnetInfo(empty) }, + ) +} diff --git a/subnet/grpc/types.go b/subnet/grpc/types.go new file mode 100644 index 0000000..60ccf36 --- /dev/null +++ b/subnet/grpc/types.go @@ -0,0 +1,19 @@ +package subnet + +import ( + refs "github.com/nspcc-dev/neofs-api-go/v2/refs/grpc" +) + +// SetID returns identifier of the subnet. Nil arg is equivalent to zero subnet ID. +func (x *SubnetInfo) SetID(id *refs.SubnetID) { + if x != nil { + x.Id = id + } +} + +// SetOwner sets subnet owner's ID in NeoFS system. +func (x *SubnetInfo) SetOwner(id *refs.OwnerID) { + if x != nil { + x.Owner = id + } +} diff --git a/subnet/info.go b/subnet/info.go new file mode 100644 index 0000000..0b4cd16 --- /dev/null +++ b/subnet/info.go @@ -0,0 +1,150 @@ +package subnet + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/refs" + refsgrpc "github.com/nspcc-dev/neofs-api-go/v2/refs/grpc" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/grpc" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/message" + subnet "github.com/nspcc-dev/neofs-api-go/v2/subnet/grpc" + protoutil "github.com/nspcc-dev/neofs-api-go/v2/util/proto" +) + +// Info represents information about NeoFS subnet. Structure is compatible with NeoFS API V2 protocol. +// +// Zero value represents zero subnet w/o an owner. +type Info struct { + id *refs.SubnetID + + owner *refs.OwnerID +} + +// ID returns identifier of the subnet. Nil return is equivalent to zero subnet ID. +func (x *Info) ID() *refs.SubnetID { + return x.id +} + +// SetID returns identifier of the subnet. Nil arg is equivalent to zero subnet ID. +func (x *Info) SetID(id *refs.SubnetID) { + x.id = id +} + +// Owner returns subnet owner's ID in NeoFS system. +func (x *Info) Owner() *refs.OwnerID { + return x.owner +} + +// SetOwner sets subnet owner's ID in NeoFS system. +func (x *Info) SetOwner(id *refs.OwnerID) { + x.owner = id +} + +// ToGRPCMessage forms subnet.SubnetInfo message and returns it as grpc.Message. +func (x *Info) ToGRPCMessage() grpc.Message { + var m *subnet.SubnetInfo + + if x != nil { + m = new(subnet.SubnetInfo) + + m.SetID(x.id.ToGRPCMessage().(*refsgrpc.SubnetID)) + m.SetOwner(x.owner.ToGRPCMessage().(*refsgrpc.OwnerID)) + } + + return m +} + +// FromGRPCMessage restores Info from grpc.Message. +// +// Supported types: +// * subnet.SubnetInfo. +func (x *Info) FromGRPCMessage(m grpc.Message) error { + v, ok := m.(*subnet.SubnetInfo) + if !ok { + return message.NewUnexpectedMessageType(m, v) + } + + var err error + + id := v.GetId() + if id == nil { + x.id = nil + } else { + if x.id == nil { + x.id = new(refs.SubnetID) + } + + err = x.id.FromGRPCMessage(id) + if err != nil { + return err + } + } + + ownerID := v.GetOwner() + if ownerID == nil { + x.owner = nil + } else { + if x.owner == nil { + x.owner = new(refs.OwnerID) + } + + err = x.owner.FromGRPCMessage(ownerID) + if err != nil { + return err + } + } + + return nil +} + +// SubnetInfo message field numbers +const ( + _ = iota + subnetInfoIDFNum + subnetInfoOwnerFNum +) + +// StableMarshal marshals Info to NeoFS API V2 binary format (Protocol Buffers with direct field order). +// +// Returns a slice of recorded data. Data is written to the provided buffer if there is enough space. +func (x *Info) StableMarshal(buf []byte) ([]byte, error) { + if x == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, x.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = protoutil.NestedStructureMarshal(subnetInfoIDFNum, buf[offset:], x.id) + if err != nil { + return nil, err + } + + offset += n + + _, err = protoutil.NestedStructureMarshal(subnetInfoOwnerFNum, buf[offset:], x.owner) + if err != nil { + return nil, err + } + + return buf, nil +} + +// StableSize returns the number of bytes required to write Info in NeoFS API V2 binary format (see StableMarshal). +func (x *Info) StableSize() (size int) { + if x != nil { + size += protoutil.NestedStructureSize(subnetInfoIDFNum, x.id) + size += protoutil.NestedStructureSize(subnetInfoOwnerFNum, x.owner) + } + + return +} + +// Unmarshal decodes Info from NeoFS API V2 binary format (see StableMarshal). +func (x *Info) Unmarshal(data []byte) error { + return message.Unmarshal(x, data, new(subnet.SubnetInfo)) +} diff --git a/subnet/test/generate.go b/subnet/test/generate.go new file mode 100644 index 0000000..a62482d --- /dev/null +++ b/subnet/test/generate.go @@ -0,0 +1,17 @@ +package subnettest + +import ( + refstest "github.com/nspcc-dev/neofs-api-go/v2/refs/test" + "github.com/nspcc-dev/neofs-api-go/v2/subnet" +) + +func GenerateSubnetInfo(empty bool) *subnet.Info { + m := new(subnet.Info) + + if !empty { + m.SetID(refstest.GenerateSubnetID(false)) + m.SetOwner(refstest.GenerateOwnerID(false)) + } + + return m +}