[#1223] lens/tui: Abandon parsers

Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
Aleksey Savchuk 2024-07-31 14:39:59 +03:00
parent 08f9fc4738
commit c72bc6596a
No known key found for this signature in database
23 changed files with 784 additions and 1118 deletions

View file

@ -0,0 +1,106 @@
package schema
import (
"errors"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/bucket"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/common"
)
type HandlerOutput struct {
Result common.Result
Next Handler
}
type Handler func(key, val []byte) (HandlerOutput, error)
func NewHandler(options ...HandlerOutput) Handler {
return func(key, val []byte) (HandlerOutput, error) {
for _, opt := range options {
if err := common.TryDecode(key, val, opt.Result); err == nil {
if opt.Next == nil {
opt.Next = NewHandler()
}
return opt, nil
}
}
return HandlerOutput{}, errors.New("no handler matched")
}
}
var x = []HandlerOutput{
{
Result: &bucket.Graveyard{},
},
{
Result: &bucket.Garbage{},
},
{
Result: &bucket.ContainerVolume{},
},
{
Result: &bucket.Locked{},
Next: NewHandler(
HandlerOutput{
Result: &bucket.LockedSubbucket{},
},
),
},
{
Result: &bucket.ShardInfo{},
},
{
Result: &bucket.Object{},
// Next: NewHandler(
// HandlerOutput{
// Result: &record.Object{},
// },
// ),
},
{
Result: &bucket.Small{},
},
{
Result: &bucket.Root{},
},
{
Result: &bucket.Owner{},
Next: NewHandler(
HandlerOutput{
Result: &bucket.OwnerSubbucket{},
},
),
},
{
Result: &bucket.Attribute{},
Next: NewHandler(
HandlerOutput{
Result: &bucket.AttributeSubbucket{},
},
),
},
{
Result: &bucket.PayloadHash{},
// Next: NewHandler(
// HandlerOutput{
// Result: &record.PayloadHash{},
// },
// ),
},
{
Result: &bucket.Parent{},
},
{
Result: &bucket.Split{},
},
{
Result: &bucket.ContainerCounters{},
},
{
Result: &bucket.ECInfo{},
},
}
func Start() Handler {
return NewHandler(x...)
}

View file

@ -0,0 +1,44 @@
package bucket
import (
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/filter"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
)
var (
_ (filter.HasCID) = (*Object)(nil)
_ (filter.HasCID) = (*Small)(nil)
_ (filter.HasCID) = (*Root)(nil)
_ (filter.HasCID) = (*PayloadHash)(nil)
_ (filter.HasCID) = (*Parent)(nil)
_ (filter.HasCID) = (*Split)(nil)
_ (filter.HasCID) = (*ECInfo)(nil)
)
func (b *Object) HasCID(id cid.ID) bool {
return b.id.Equals(id)
}
func (b *Small) HasCID(id cid.ID) bool {
return b.id.Equals(id)
}
func (b *Root) HasCID(id cid.ID) bool {
return b.id.Equals(id)
}
func (b *PayloadHash) HasCID(id cid.ID) bool {
return b.id.Equals(id)
}
func (b *Parent) HasCID(id cid.ID) bool {
return b.id.Equals(id)
}
func (b *Split) HasCID(id cid.ID) bool {
return b.id.Equals(id)
}
func (b *ECInfo) HasCID(id cid.ID) bool {
return b.id.Equals(id)
}

View file

@ -0,0 +1,75 @@
package bucket
import "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/common"
func (b *Graveyard) String() string {
return b.prefix.String()
}
func (b *Garbage) String() string {
return b.prefix.String()
}
func (b *ContainerVolume) String() string {
return b.prefix.String()
}
func (b *Locked) String() string {
return b.prefix.String()
}
func (b *LockedSubbucket) String() string {
return b.id.String()
}
func (b *ShardInfo) String() string {
return b.prefix.String()
}
func (b *Object) String() string {
return common.JoinWithSpace(b.prefix, b.id)
}
func (b *Small) String() string {
return common.JoinWithSpace(b.prefix, b.id)
}
func (b *Root) String() string {
return common.JoinWithSpace(b.prefix, b.id)
}
func (b *Owner) String() string {
return common.JoinWithSpace(b.prefix, b.id)
}
func (b *OwnerSubbucket) String() string {
return b.id.String()
}
func (b *Attribute) String() string {
return common.JoinWithSpace(b.prefix, b.id, b.key)
}
func (b *AttributeSubbucket) String() string {
return b.val
}
func (b *PayloadHash) String() string {
return common.JoinWithSpace(b.prefix, b.id)
}
func (b *Parent) String() string {
return common.JoinWithSpace(b.prefix, b.id)
}
func (b *Split) String() string {
return common.JoinWithSpace(b.prefix, b.id)
}
func (b *ContainerCounters) String() string {
return b.prefix.String()
}
func (b *ECInfo) String() string {
return common.JoinWithSpace(b.prefix, b.id)
}

View file

@ -0,0 +1,322 @@
package bucket
import (
"errors"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/common"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/mr-tron/base58"
)
var (
ErrInvalidLength = errors.New("invalid name length")
ErrInvalidPrefix = errors.New("invalid name prefix")
)
type (
Graveyard struct {
prefix common.Prefix
}
Garbage struct {
prefix common.Prefix
}
ContainerVolume struct {
prefix common.Prefix
}
Locked struct {
prefix common.Prefix
}
LockedSubbucket struct {
id cid.ID
}
ShardInfo struct {
prefix common.Prefix
}
Object struct {
prefix common.Prefix
id cid.ID
}
Small struct {
prefix common.Prefix
id cid.ID
}
Root struct {
prefix common.Prefix
id cid.ID
}
Owner struct {
prefix common.Prefix
id cid.ID
}
OwnerSubbucket struct {
id user.ID
}
Attribute struct {
prefix common.Prefix
id cid.ID
key string
}
AttributeSubbucket struct {
val string
}
PayloadHash struct {
prefix common.Prefix
id cid.ID
}
Parent struct {
prefix common.Prefix
id cid.ID
}
Split struct {
prefix common.Prefix
id cid.ID
}
ContainerCounters struct {
prefix common.Prefix
}
ECInfo struct {
prefix common.Prefix
id cid.ID
}
)
func (b *Graveyard) Decode(name, _ []byte) (err error) {
if len(name) != 1 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.Graveyard {
return ErrInvalidPrefix
}
return nil
}
func (b *Garbage) Decode(name, _ []byte) (err error) {
if len(name) != 1 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.Garbage {
return ErrInvalidPrefix
}
return nil
}
func (b *ContainerVolume) Decode(name, _ []byte) (err error) {
if len(name) != 1 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.ContainerVolume {
return ErrInvalidPrefix
}
return nil
}
func (b *Locked) Decode(name, _ []byte) (err error) {
if len(name) != 1 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.Locked {
return ErrInvalidPrefix
}
return nil
}
func (b *LockedSubbucket) Decode(name, _ []byte) (err error) {
if len(name) != 32 {
return ErrInvalidLength
}
if err = b.id.Decode(name); err != nil {
return err
}
return nil
}
func (b *ShardInfo) Decode(name, _ []byte) (err error) {
if len(name) != 1 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.ShardInfo {
return ErrInvalidPrefix
}
return nil
}
func (b *Object) Decode(name, _ []byte) (err error) {
if len(name) != 33 {
return ErrInvalidLength
}
switch b.prefix = common.Prefix(name[0]); b.prefix {
case common.Primary:
case common.Lockers:
case common.Tombstone:
default:
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:]); err != nil {
return err
}
return nil
}
func (b *Small) Decode(name, _ []byte) (err error) {
if len(name) != 33 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.Small {
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:]); err != nil {
return err
}
return nil
}
func (b *Root) Decode(name, _ []byte) (err error) {
if len(name) != 33 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.Root {
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:]); err != nil {
return err
}
return nil
}
func (b *Owner) Decode(name, _ []byte) (err error) {
if len(name) != 33 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.Owner {
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:]); err != nil {
return err
}
return nil
}
func (b *OwnerSubbucket) Decode(name, _ []byte) (err error) {
if err = b.id.DecodeString(base58.Encode(name)); err != nil {
return err
}
return nil
}
func (b *Attribute) Decode(name, _ []byte) (err error) {
if len(name) < 34 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.UserAttribute {
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:33]); err != nil {
return err
}
b.key = string(name[33:])
return nil
}
func (b *AttributeSubbucket) Decode(name, _ []byte) (err error) {
if len(name) == 0 {
return ErrInvalidLength
}
b.val = string(name)
return nil
}
func (b *PayloadHash) Decode(name, _ []byte) (err error) {
if len(name) != 33 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.PayloadHash {
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:]); err != nil {
return err
}
return nil
}
func (b *Parent) Decode(name, _ []byte) (err error) {
if len(name) != 33 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.Parent {
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:]); err != nil {
return err
}
return nil
}
func (b *Split) Decode(name, _ []byte) (err error) {
if len(name) != 33 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.Split {
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:]); err != nil {
return err
}
return nil
}
func (b *ContainerCounters) Decode(name, _ []byte) (err error) {
if len(name) != 1 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.ContainerCounters {
return ErrInvalidPrefix
}
return nil
}
func (b *ECInfo) Decode(name, _ []byte) (err error) {
if len(name) != 33 {
return ErrInvalidLength
}
if b.prefix = common.Prefix(name[0]); b.prefix != common.ECInfo {
return ErrInvalidPrefix
}
if err = b.id.Decode(name[1:]); err != nil {
return err
}
return nil
}

View file

@ -1,6 +1,4 @@
//go:generate stringer -type=Prefix
package types
package common
import "fmt"

View file

@ -0,0 +1,15 @@
package common
type Result interface {
String() string
// DetailedString() string
Decode(key, value []byte) error
}
func TryDecode[T Result](key, value []byte, x T) error {
if err := x.Decode(key, value); err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,53 @@
package common
import (
"fmt"
"strings"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/nspcc-dev/neo-go/pkg/io"
)
func DecodeOIDs(data []byte) ([]oid.ID, error) {
r := io.NewBinReaderFromBuf(data)
size := r.ReadVarUint()
oids := make([]oid.ID, size)
var i uint64
for ; i < size; i++ {
if err := oids[i].Decode(r.ReadVarBytes()); err != nil {
return nil, err
}
}
return oids, nil
}
func JoinWithSpace(xs ...any) string {
if len(xs) == 0 {
return ""
}
b := strings.Builder{}
xs, last := xs[:len(xs)-1], xs[len(xs)-1]
for _, x := range xs {
b.WriteString(getString(x))
b.WriteString(" ")
}
b.WriteString(getString(last))
return b.String()
}
func getString(x any) string {
switch y := x.(type) {
case string:
return y
case fmt.Stringer:
return y.String()
default:
panic("can't create joined string, got neither string nor Stringer")
}
}

View file

@ -0,0 +1,22 @@
package filter
import (
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
type HasCID interface {
HasCID(id cid.ID) bool
}
type HasOID interface {
HasOID(id oid.ID) bool
}
type HasAttrKey interface {
HasAttrKey(key string) bool
}
type HasAttrVal interface {
HasAttrVal(val string) bool
}

View file

@ -0,0 +1,12 @@
package record
import (
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/filter"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
var _ (filter.HasOID) = (*Object)(nil)
func (r *Object) HasOID(id oid.ID) bool {
return r.id.Equals(id)
}

View file

@ -0,0 +1,57 @@
package record
import (
"errors"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/common"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
type Record interface {
Decode(key []byte, val []byte) (err error)
}
var (
ErrInvalidKeyLength = errors.New("invalid key length")
ErrInvalidValLength = errors.New("invalid value length")
)
type (
Object struct {
id oid.ID
object objectSDK.Object
}
PayloadHash struct {
checksum checksum.Checksum
ids []oid.ID
}
)
func (r *Object) Decode(key []byte, val []byte) (err error) {
if len(key) != 32 {
return ErrInvalidKeyLength
}
if err = r.id.Decode(key); err != nil {
return err
}
if err = r.object.Unmarshal(val); err != nil {
return err
}
return nil
}
func (r *PayloadHash) Decode(key []byte, val []byte) (err error) {
if len(key) != 32 {
return ErrInvalidKeyLength
}
r.checksum.SetSHA256([32]byte(key))
if r.ids, err = common.DecodeOIDs(val); err != nil {
return err
}
return nil
}

View file

@ -4,7 +4,8 @@ import (
"context"
"fmt"
handlers "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/schema"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/common"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/davecgh/go-spew/spew"
@ -24,8 +25,8 @@ type Bucket struct {
name []byte
path [][]byte
nextHandler handlers.Handler
result *handlers.KV
nextHandler schema.Handler
result common.Result
}
type Record struct {
@ -33,7 +34,7 @@ type Record struct {
value []byte
path [][]byte
// nextHandler handlers.Handler
result *handlers.KV
result common.Result
}
type Application struct {

View file

@ -2,30 +2,30 @@ package tuiutil
import (
"context"
"errors"
"fmt"
handlers "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/schema"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func (a *Application) newBucketsView(ctx context.Context, cnr *cid.ID, obj *oid.ID) (tview.Primitive, error) {
func (a *Application) newBucketsView(ctx context.Context, _ *cid.ID, _ *oid.ID) (tview.Primitive, error) {
tree := tview.NewTreeView()
tree.
SetBorder(true).
SetTitle(a.db.Path())
var filters []handlers.Filter
if cnr != nil {
filters = append(filters, handlers.WithCID(*cnr))
}
if obj != nil {
filters = append(filters, handlers.WithOID(*obj))
}
// var filters []handlers.Filter
// if cnr != nil {
// filters = append(filters, handlers.WithCID(*cnr))
// }
// if obj != nil {
// filters = append(filters, handlers.WithOID(*obj))
// }
handler := handlers.GetHandler(filters...)
handler := schema.Start()
root := tview.NewTreeNode(".")
root.
@ -34,11 +34,11 @@ func (a *Application) newBucketsView(ctx context.Context, cnr *cid.ID, obj *oid.
SetReference(&Bucket{nextHandler: handler})
var err error
if len(filters) == 0 {
err = a.getChildren(ctx, root, true)
} else {
err = a.getChildren(ctx, root, false)
}
// if len(filters) == 0 {
err = a.getChildren(ctx, root, true)
// } else {
// err = a.getChildren(ctx, root, false)
// }
if err != nil {
return nil, err
}
@ -90,17 +90,24 @@ func (a *Application) getChildren(ctx context.Context, parent *tview.TreeNode, l
}
for bucket := range buffer {
repr, next, err := handler(bucket.name, nil)
if errors.Is(err, handlers.ErrFilter) {
continue
}
res, err := handler(bucket.name, nil)
// if errors.Is(err, handlers.ErrFilter) {
// continue
// }
if err != nil {
return err
continue
// return err
}
bucket.nextHandler = next
bucket.result = repr
bucket.nextHandler = res.Next
bucket.result = res.Result
child := tview.NewTreeNode(repr.Key.String())
var child *tview.TreeNode
switch z := res.Result.(type) {
case fmt.Stringer:
child = tview.NewTreeNode(z.String())
default:
child = tview.NewTreeNode(fmt.Sprintf("%T", res.Result))
}
child.SetSelectable(true)
child.SetExpanded(false)
@ -118,12 +125,13 @@ func (a *Application) getChildren(ctx context.Context, parent *tview.TreeNode, l
found := false
for record := range records {
_, _, err = next(record.key, record.value)
if errors.Is(err, handlers.ErrFilter) {
continue
}
_, err = res.Next(record.key, record.value)
// if errors.Is(err, handlers.ErrFilter) {
// continue
// }
if err != nil {
return err
continue
// return err
}
found = true
break

View file

@ -1,28 +1,21 @@
package tuiutil
import (
"context"
// func (a *Application) newDetailedView(_ context.Context, rec *Record) (tview.Primitive, error) {
// view := tview.NewTextView()
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
// view.
// SetText(rec.result.Val.DetailedString()).
// SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
// if event.Key() == tcell.KeyBacktab {
// a.nav.Pop()
// return nil
// }
// return event
// })
func (a *Application) newDetailedView(_ context.Context, rec *Record) (tview.Primitive, error) {
view := tview.NewTextView()
// view.
// SetBorder(true).
// SetTitle(rec.result.Key.String())
view.
SetText(rec.result.Val.DetailedString()).
SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyBacktab {
a.nav.Pop()
return nil
}
return event
})
view.
SetBorder(true).
SetTitle(rec.result.Key.String())
return view, nil
}
// return view, nil
// }

View file

@ -2,13 +2,10 @@ package tuiutil
import (
"context"
"errors"
"fmt"
"os"
"sync/atomic"
"time"
handlers "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/schema"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
@ -25,23 +22,24 @@ func (a *Application) newRecordsView(ctx context.Context, bkt *Bucket) (tview.Pr
defer close(records)
for record := range temp {
result, _, err := bkt.nextHandler(record.key, record.value)
if errors.Is(err, handlers.ErrFilter) {
continue
}
res, err := bkt.nextHandler(record.key, record.value)
// if errors.Is(err, handlers.ErrFilter) {
// continue
// }
if err != nil {
a.stopOnErr(err)
return
continue
// a.stopOnErr(err)
// return
}
// record.nextHandler = next
record.result = result
record.result = res.Result
records <- record
}
}()
box := tview.NewBox().
SetBorder(true).
SetTitle(bkt.result.Key.String())
SetTitle(fmt.Sprintf("%T", bkt.result))
return &RecordsView{
Box: box,
@ -91,7 +89,6 @@ func (v *RecordsView) process() {
if !ok {
break
}
fmt.Fprintln(os.Stderr, record.result.Key)
v.records = append(v.records, record)
}
@ -118,7 +115,7 @@ func (v *RecordsView) draw(screen tcell.Screen) {
v.DrawForSubclass(screen, v)
for index := v.firstRecordIndex; index < v.lastRecordIndex; index++ {
result := v.records[index].result
text := fmt.Sprintf("%s | %s", result.Key, result.Val)
text := fmt.Sprintf("%s | %s", fmt.Sprintf("%T", result), "some val")
if index == v.selectedRecordIndex {
text = fmt.Sprintf("[:white]%s[:black]", text)
}
@ -134,7 +131,6 @@ func (v *RecordsView) Draw(screen tcell.Screen) {
go func() {
v.process()
ready <- struct{}{}
fmt.Fprintln(os.Stderr, "done")
}()
select {
@ -153,10 +149,8 @@ func (v *RecordsView) Draw(screen tcell.Screen) {
_ = v.app.nav.Push(context.Background(), func(ctx context.Context) (tview.Primitive, error) {
return loadingScreen.Start(ctx), nil
})
fmt.Fprintln(os.Stderr, "pushed")
<-ready
v.app.nav.Pop()
fmt.Fprintln(os.Stderr, "pop")
}()
}
}
@ -224,15 +218,15 @@ func (v *RecordsView) InputHandler() func(event *tcell.EventKey, _ func(p tview.
v.moveToPrevPage()
v.selectLastRecord()
}
case tcell.KeyTab:
record := v.getSelectedItem()
if record == nil {
return
}
err := v.app.nav.Push(context.Background(), func(ctx context.Context) (tview.Primitive, error) {
return v.app.newDetailedView(ctx, record)
})
v.app.stopOnErr(err)
// case tcell.KeyTab:
// record := v.getSelectedItem()
// if record == nil {
// return
// }
// err := v.app.nav.Push(context.Background(), func(ctx context.Context) (tview.Primitive, error) {
// return v.app.newDetailedView(ctx, record)
// })
// v.app.stopOnErr(err)
case tcell.KeyBacktab:
v.app.nav.Pop()
default:

View file

@ -1,515 +0,0 @@
package schema
import (
"errors"
"fmt"
"strconv"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/schema/parse"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/schema/parse/types"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
type KV struct {
Key parse.Result
Val parse.Result
}
type Handler func(key, value []byte) (repr *KV, next Handler, err error)
func NewHandler(
keyParser parse.Parser[parse.Result],
valParser parse.Parser[parse.Result],
next Handler,
) Handler {
return func(key, value []byte) (repr *KV, _ Handler, err error) {
var keyRepr parse.Result = parse.NewRawResult("")
var valueRepr parse.Result = parse.NewRawResult("")
if keyParser != nil {
keyRepr, _, err = keyParser(key)
if err != nil {
return nil, nil, fmt.Errorf("can't parse key: %w", err)
}
}
if valParser != nil {
valueRepr, _, err = valParser(value)
if err != nil {
return nil, nil, fmt.Errorf("can't parse value: %w", err)
}
}
return &KV{keyRepr, valueRepr}, next, nil
}
}
func Any(handlers ...Handler) Handler {
return func(key, value []byte) (repr *KV, next Handler, err error) {
var errs error
for _, handler := range handlers {
repr, next, err = handler(key, value)
if err == nil {
return repr, next, nil
}
errs = errors.Join(errs, err)
}
return nil, nil, errs
}
}
func WithFallback(handler Handler, fallback Handler) Handler {
if handler == nil {
return fallback
}
return func(key, value []byte) (repr *KV, next Handler, err error) {
repr, next, err = handler(key, value)
if err == nil {
return repr, WithFallback(next, fallback), nil
}
repr, next, err = fallback(key, value)
if err == nil {
return repr, WithFallback(next, fallback), nil
}
panic(fmt.Errorf("fallback handler returned an error: %w", err))
}
}
func ErrorHandler(handler Handler) Handler {
if handler == nil {
return nil
}
return func(key, value []byte) (repr *KV, next Handler, err error) {
repr, next, err = handler(key, value)
if err != nil {
repr = &KV{
Key: parse.NewRawResult(err.Error()),
Val: parse.NewRawResult(""),
}
}
return repr, ErrorHandler(next), nil
}
}
type Filter func(cfg *HandlerConfig)
type HandlerConfig struct {
cidP parse.Parser[types.CID]
oidP parse.Parser[types.OID]
}
func GetHandler(filters ...Filter) Handler {
cfg := &HandlerConfig{
cidP: parse.CID,
oidP: parse.OID,
}
for _, filter := range filters {
filter(cfg)
}
handler := Any(
GetInhumeBucketsHandler(cfg),
GetContainerVolumeHandler(cfg),
GetLockedBucketsHandler(cfg),
GetShardInfoHandler(cfg),
GetObjectBucketsHandler(cfg),
GetSmallBucketsHandler(cfg),
GetRootBucketsHandler(cfg),
GetOwnerBucketsHandler(cfg),
GetAttributeBucketsHandler(cfg),
GetPayloadHashBucketHandler(cfg),
GetParentBucketsHandler(cfg),
GetSplitBucketsHandler(cfg),
GetContainerCountersHandler(cfg),
GetECInfoBucketHandler(cfg),
)
if len(filters) > 0 {
handler = FilterFailHandler(handler)
} else {
handler = WithFallback(handler, RawHandler)
}
return handler
}
func WithCID(id cid.ID) Filter {
return func(cfg *HandlerConfig) {
cfg.cidP = parse.Exact(parse.CID, types.CID{ID: id})
}
}
func WithOID(id oid.ID) Filter {
return func(cfg *HandlerConfig) {
cfg.oidP = parse.Exact(parse.OID, types.OID{ID: id})
}
}
func RawHandler(key, value []byte) (repr *KV, next Handler, err error) {
return &KV{
parse.NewRawResult(fmt.Sprintf("%v", key)),
parse.NewRawResult(fmt.Sprintf("%v", value)),
}, RawHandler, nil
}
var ErrFilter = errors.New("it doesn't match the filter")
func FilterFailHandler(h Handler) Handler {
return func(key, value []byte) (repr *KV, next Handler, err error) {
repr, next, err = h(key, value)
if err != nil {
return nil, nil, ErrFilter
}
return repr, FilterFailHandler(next), nil
}
}
func GetInhumeBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Any(
parse.Prefix(types.Graveyard),
parse.Prefix(types.Garbage),
)),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.cidP),
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.Any(
parse.Collect(parse.Chain(
parse.Finalize(parse.CID),
parse.Finalize(parse.OID),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.Skip),
)),
nil,
),
)
}
func GetContainerVolumeHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.ContainerVolume)),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Collect(parse.Chain(
parse.Finalize(parse.Volume),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}
func GetLockedBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.Locked)),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Collect(parse.Chain(
parse.Finalize(parse.OIDsList),
parse.Finalize(parse.EOF),
)),
nil,
),
),
)
}
func GetShardInfoHandler(_ *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.ShardInfo)),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Map(
parse.Any(
parse.ExactString("id"),
parse.ExactString("version"),
parse.ExactString("logic_counter"),
parse.ExactString("phy_counter"),
parse.ExactString("user_counter"),
),
func(x string) parse.Result {
return parse.NewRawResult(x, func(s string) string { return fmt.Sprintf("%13s", s) })
},
),
parse.Finalize(parse.EOF),
)),
parse.Map(
parse.Uint64,
func(x uint64) parse.Result { return parse.NewRawResult(strconv.FormatUint(x, 10)) },
),
nil,
),
)
}
func GetObjectBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Any(
parse.Prefix(types.Primary),
parse.Prefix(types.Lockers),
parse.Prefix(types.Tombstone),
)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Collect(parse.Chain(
parse.Finalize(parse.Object),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}
func GetSmallBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.Small)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.Any(
parse.Finalize(parse.StorageID),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}
func GetRootBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.Root)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.Any(
parse.Collect(parse.Chain(
parse.Finalize(parse.SplitInfo),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}
func GetAttributeBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.UserAttribute)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.AttributeKey),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.AttributeValue),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.Skip),
nil,
),
),
)
}
func GetOwnerBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.Owner)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.UID),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.Skip),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.Skip),
nil,
),
),
)
}
func GetPayloadHashBucketHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.PayloadHash)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Checksum),
parse.Finalize(parse.EOF),
)),
parse.Collect(parse.Chain(
parse.Finalize(parse.OIDsList),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}
func GetParentBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.Parent)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Collect(parse.Chain(
parse.Finalize(parse.OIDsList),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}
func GetSplitBucketsHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.Split)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.UUID),
parse.Finalize(parse.EOF),
)),
parse.Collect(parse.Chain(
parse.Finalize(parse.OIDsList),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}
func GetContainerCountersHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.ContainerCounters)),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Collect(parse.Chain(
parse.Finalize(parse.ObjectCounters),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}
func GetECInfoBucketHandler(cfg *HandlerConfig) Handler {
return NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(parse.Prefix(types.ECInfo)),
parse.Finalize(cfg.cidP),
parse.Finalize(parse.EOF),
)),
parse.Finalize(parse.EOF),
NewHandler(
parse.Collect(parse.Chain(
parse.Finalize(cfg.oidP),
parse.Finalize(parse.EOF),
)),
parse.Collect(parse.Chain(
parse.Finalize(parse.OIDsList),
parse.Finalize(parse.EOF),
)),
nil,
),
)
}

View file

@ -1,280 +0,0 @@
package parse
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/schema/parse/types"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/io"
)
type Parser[T any] func(data []byte) (result T, rest []byte, err error)
func Any[T any](parsers ...Parser[T]) Parser[T] {
return func(data []byte) (result T, rest []byte, err error) {
var zero T
var errs error
for _, parser := range parsers {
result, rest, err = parser(data)
if err == nil {
return result, rest, nil
}
errs = errors.Join(errs, err)
}
return zero, data, errs
}
}
func Chain[T any](parsers ...Parser[T]) Parser[[]T] {
return func(data []byte) (_ []T, rest []byte, err error) {
var result T
results := make([]T, len(parsers))
rest = data
for index, parser := range parsers {
result, rest, err = parser(rest)
if err != nil {
return nil, data, err
}
results[index] = result
}
return results, rest, nil
}
}
func Map[T, U any](parser Parser[T], f func(T) U) Parser[U] {
return func(data []byte) (_ U, rest []byte, err error) {
var zero U
var result T
result, rest, err = parser(data)
if err != nil {
return zero, data, err
}
return f(result), rest, nil
}
}
func If[T any](parser Parser[T], predicate func(T) bool) Parser[T] {
return func(data []byte) (result T, rest []byte, err error) {
var zero T
result, rest, err = parser(data)
if err != nil {
return zero, data, err
}
if !predicate(result) {
return zero, data, errors.New("predicate returned false")
}
return result, rest, nil
}
}
func Exact[T comparable](parser Parser[T], value T) Parser[T] {
return If(parser, func(v T) bool { return v == value })
}
func Finalize[T fmt.Stringer](p Parser[T], formatters ...func(string) string) Parser[Result] {
return func(data []byte) (result Result, rest []byte, err error) {
var x T
x, rest, err = p(data)
if err != nil {
return nil, data, err
}
return NewResultValue(x, formatters...), rest, nil
}
}
func Collect(parser Parser[[]Result]) Parser[Result] {
return func(data []byte) (result Result, rest []byte, err error) {
var results []Result
results, rest, err = parser(data)
if err != nil {
return nil, data, err
}
return &ResultArray{results: results}, rest, nil
}
}
func Fail(data []byte) (result Result, rest []byte, err error) {
return nil, data, errors.New("it's a parser that always fails")
}
// func ExpectEOF[T any](parser Parser[T]) Parser[T] {
// return func(data []byte) (result T, rest []byte, err error) {
// var zero T
// result, rest, err = parser(data)
// if err != nil {
// return zero, data, err
// }
// if len(rest) > 0 {
// return zero, data, fmt.Errorf("expected EOF but got %v", rest)
// }
// return result, nil, nil
// }
// }
func Skip(_ []byte) (result types.Empty, rest []byte, err error) {
return types.Empty{}, nil, nil
}
func EOF(data []byte) (result types.Empty, rest []byte, err error) {
if len(data) != 0 {
return types.Empty{}, data, fmt.Errorf("expected EOF but got %v", data)
}
return types.Empty{}, nil, nil
}
func Prefix(prefix types.Prefix) Parser[types.Prefix] {
return func(data []byte) (result types.Prefix, rest []byte, err error) {
var zero types.Prefix
if len(data) == 0 {
return zero, data, fmt.Errorf("expected prefix %d but got EOF", prefix)
}
if data[0] != byte(prefix) {
return zero, data, fmt.Errorf("expected prefix %d but got %d", prefix, data[0])
}
return prefix, data[1:], nil
}
}
func CID(data []byte) (result types.CID, rest []byte, err error) {
if len(data) < 32 {
return types.CID{}, data, errors.New("can't parse CID, not enough data")
}
id := cid.ID{}
err = id.Decode(data[:32])
if err != nil {
return types.CID{}, data, fmt.Errorf("can't parse CID: %w", err)
}
return types.CID{ID: id}, data[32:], nil
}
func OID(data []byte) (result types.OID, rest []byte, err error) {
if len(data) < 32 {
return types.OID{}, data, errors.New("can't parse OID, not enough data")
}
id := oid.ID{}
err = id.Decode(data[:32])
if err != nil {
return types.OID{}, data, fmt.Errorf("can't parse OID: %w", err)
}
return types.OID{ID: id}, data[32:], nil
}
func UID(data []byte) (result types.UID, rest []byte, err error) {
id := user.ID{}
if err = id.DecodeString(string(data)); err != nil {
return types.UID{}, data, fmt.Errorf("can't parse UID: %w", err)
}
return types.UID{ID: id}, nil, nil
}
func Object(data []byte) (result types.Object, rest []byte, err error) {
obj := objectSDK.New()
if err = obj.Unmarshal(data); err != nil {
return types.Object{}, data, fmt.Errorf("can't parse object: %w", err)
}
return types.Object{Obj: obj}, nil, nil
}
func Uint64(data []byte) (result uint64, rest []byte, err error) {
if len(data) < 8 {
return 0, data, errors.New("can't parse uint64, not enough data")
}
return binary.LittleEndian.Uint64(data[:8]), data[8:], nil
}
func String(data []byte) (result string, rest []byte, err error) {
if len(data) == 0 {
return "", data, errors.New("can't parse string, unexpected EOF")
}
return string(data), nil, nil
}
func Checksum(data []byte) (result types.Checksum, next []byte, err error) {
if len(data) < 32 {
return types.Checksum{}, data, errors.New("can't parse checksum, not enough data")
}
return types.Checksum{Value: data[:32]}, data[32:], nil
}
func OIDsList(data []byte) (result types.OIDsList, rest []byte, err error) {
r := io.NewBinReaderFromBuf(data)
size := r.ReadVarUint()
oids := make([]types.OID, size)
var i uint64
for ; i < size; i++ {
id := oid.ID{}
if err = id.Decode(r.ReadVarBytes()); err != nil {
return types.OIDsList{}, data, fmt.Errorf("can't parse list of OIDs: %w", err)
}
oids[i] = types.OID{ID: id}
}
return types.OIDsList{OIDs: oids}, nil, nil
}
func UUID(data []byte) (result types.UUID, rest []byte, err error) {
if len(data) < 16 {
return types.UUID{}, data, errors.New("can't parse UUID, not enough data")
}
id := uuid.UUID{}
if err := id.UnmarshalBinary(data[:16]); err != nil {
return types.UUID{}, data, fmt.Errorf("can't parsse UUID: %w", err)
}
return types.UUID{ID: id}, data[16:], nil
}
func SplitInfo(data []byte) (result types.SplitInfo, rest []byte, err error) {
info := objectSDK.NewSplitInfo()
if err = info.Unmarshal(data); err != nil {
return types.SplitInfo{}, data, fmt.Errorf("can't parse split info: %w", err)
}
return types.SplitInfo{Info: info}, nil, nil
}
var AttributeKey = Map(String, func(s string) types.AttributeKey {
return types.AttributeKey{Key: s}
})
var AttributeValue = Map(String, func(s string) types.AttributeValue {
return types.AttributeValue{Value: s}
})
var StorageID = Map(String, func(x string) types.StorageID {
return types.StorageID{Path: x}
})
var Volume = Map(Uint64, func(x uint64) types.Volume {
return types.Volume{Volume: x}
})
var ObjectCounters = Map(
Chain(Uint64, Uint64, Uint64),
func(x []uint64) types.ObjectCounters {
return types.ObjectCounters{
Logical: x[0],
Physical: x[1],
User: x[2],
}
},
)
func ExactString(s string) Parser[string] {
return func(data []byte) (result string, rest []byte, err error) {
rest, found := bytes.CutPrefix(data, []byte(s))
if !found {
return "", data, fmt.Errorf("can't parse string %s, string not found", s)
}
return s, rest, nil
}
}

View file

@ -1,87 +0,0 @@
package parse
import (
"fmt"
"strings"
"github.com/davecgh/go-spew/spew"
)
type Result interface {
fmt.Stringer
DetailedString() string
}
type ResultValue struct {
value fmt.Stringer
formatters []func(string) string
}
func NewResultValue(value fmt.Stringer, formatters ...func(string) string) *ResultValue {
return &ResultValue{
value: value,
formatters: formatters,
}
}
func (r *ResultValue) String() string {
s := r.value.String()
for _, f := range r.formatters {
s = f(s)
}
return s
}
func (r *ResultValue) DetailedString() string {
return spew.Sdump(r.value)
}
type ResultArray struct {
results []Result
}
func NewResultArray(results ...Result) *ResultArray {
return &ResultArray{results}
}
func (r *ResultArray) String() string {
b := strings.Builder{}
for _, result := range r.results {
_, _ = b.WriteString(result.String())
_, _ = b.WriteString(" ")
}
return b.String()
}
func (r *ResultArray) DetailedString() string {
b := strings.Builder{}
for _, result := range r.results {
_, _ = b.WriteString(result.DetailedString())
}
return b.String()
}
type RawResult struct {
s string
formatters []func(string) string
}
func NewRawResult(s string, formatters ...func(string) string) *RawResult {
return &RawResult{s: s, formatters: formatters}
}
func (r *RawResult) String() string {
s := r.s
for _, f := range r.formatters {
s = f(s)
}
return s
}
func (r *RawResult) DetailedString() string {
s := r.s
for _, f := range r.formatters {
s = f(s)
}
return s + "\n"
}

View file

@ -1,17 +0,0 @@
package types
type AttributeKey struct {
Key string
}
func (k AttributeKey) String() string {
return k.Key
}
type AttributeValue struct {
Value string
}
func (v AttributeValue) String() string {
return v.Value
}

View file

@ -1,13 +0,0 @@
package types
import (
"encoding/hex"
)
type Checksum struct {
Value []byte
}
func (cs Checksum) String() string {
return hex.EncodeToString(cs.Value)
}

View file

@ -1,27 +0,0 @@
package types
import (
"fmt"
"strconv"
)
type ObjectCounters struct {
Logical uint64
Physical uint64
User uint64
}
func (c ObjectCounters) String() string {
return fmt.Sprintf(
"logical %d, physical %d, user %d",
c.Logical, c.Physical, c.User,
)
}
type Volume struct {
Volume uint64
}
func (v Volume) String() string {
return strconv.FormatUint(v.Volume, 10)
}

View file

@ -1,7 +0,0 @@
package types
type Empty struct{}
func (e Empty) String() string {
return ""
}

View file

@ -1,63 +0,0 @@
package types
import (
"fmt"
"strings"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/google/uuid"
)
type CID struct {
ID cid.ID
}
func (id CID) String() string {
return fmt.Sprintf("CID [red]%-44s[white]", id.ID)
}
type OID struct {
ID oid.ID
}
func (id OID) String() string {
return fmt.Sprintf("OID [red]%-44s[white]", id.ID)
}
type UID struct {
ID user.ID
}
func (id UID) String() string {
return id.ID.String()
}
type OIDsList struct {
OIDs []OID
}
func (l OIDsList) String() string {
var acc []string
for _, id := range l.OIDs {
acc = append(acc, id.String())
}
return strings.TrimRight(strings.Join(acc, " "), " ")
}
type UUID struct {
ID uuid.UUID
}
func (id UUID) String() string {
return id.ID.String()
}
type StorageID struct {
Path string
}
func (id StorageID) String() string {
return id.Path
}

View file

@ -1,25 +0,0 @@
package types
import (
"fmt"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
)
type (
Object struct {
Obj *objectSDK.Object
}
SplitInfo struct {
Info *objectSDK.SplitInfo
}
)
func (o Object) String() string {
return fmt.Sprintf("%+v", o.Obj)
}
func (si SplitInfo) String() string {
return fmt.Sprintf("%+v", si.Info)
}