[#1223] lens/tui: Make parsers for each bucket/record type

Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
Aleksey Savchuk 2024-08-04 12:28:00 +03:00
parent 22170f05e9
commit 1da1824d15
No known key found for this signature in database
13 changed files with 621 additions and 756 deletions

View file

@ -2,137 +2,299 @@ package schema
import (
"errors"
"fmt"
"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"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/record"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
)
type HandlerOption struct {
Factory func() common.Result
Next Handler
}
type HandlerOutput struct {
Result common.Result
Next Handler
}
type Handler func(key, val []byte) (HandlerOutput, error)
func NewHandler(options ...HandlerOption) Handler {
return func(key, val []byte) (HandlerOutput, error) {
for _, opt := range options {
result := opt.Factory()
if err := common.TryDecode(key, val, result); err == nil {
if opt.Next == nil {
opt.Next = NewHandler()
}
return HandlerOutput{Result: result, Next: opt.Next}, nil
}
}
return HandlerOutput{}, errors.New("no handler matched")
}
}
func RawHandler(key, val []byte) (HandlerOutput, error) {
r := &common.RawResult{}
if err := r.Decode(key, val); err != nil {
return HandlerOutput{}, err
}
return HandlerOutput{Result: r, Next: RawHandler}, nil
}
func WithFallback(handler Handler, fallback Handler) Handler {
if handler == nil {
return fallback
}
return func(key, val []byte) (HandlerOutput, error) {
out, err := handler(key, val)
if err == nil {
return HandlerOutput{
Result: out.Result,
Next: WithFallback(out.Next, fallback),
}, nil
}
out, err = fallback(key, val)
if err == nil {
return HandlerOutput{
Result: out.Result,
Next: WithFallback(out.Next, fallback),
}, nil
}
panic("fallback handler returned an error")
}
}
var x = []HandlerOption{
{
Factory: func() common.Result { return &bucket.Graveyard{} },
},
{
Factory: func() common.Result { return &bucket.Garbage{} },
},
{
Factory: func() common.Result { return &bucket.ContainerVolume{} },
},
{
Factory: func() common.Result { return &bucket.Locked{} },
Next: NewHandler(
HandlerOption{Factory: func() common.Result { return &bucket.LockedSubbucket{} }},
),
},
{
Factory: func() common.Result { return &bucket.ShardInfo{} },
},
{
Factory: func() common.Result { return &bucket.Object{} },
Next: NewHandler(
HandlerOption{Factory: func() common.Result { return &record.Object{} }},
),
},
{
Factory: func() common.Result { return &bucket.Small{} },
},
{
Factory: func() common.Result { return &bucket.Root{} },
},
{
Factory: func() common.Result { return &bucket.Owner{} },
Next: NewHandler(
HandlerOption{Factory: func() common.Result { return &bucket.OwnerSubbucket{} }},
),
},
{
Factory: func() common.Result { return &bucket.Attribute{} },
Next: NewHandler(
HandlerOption{Factory: func() common.Result { return &bucket.AttributeSubbucket{} }},
),
},
{
Factory: func() common.Result { return &bucket.PayloadHash{} },
},
{
Factory: func() common.Result { return &bucket.Parent{} },
},
{
Factory: func() common.Result { return &bucket.Split{} },
},
{
Factory: func() common.Result { return &bucket.ContainerCounters{} },
},
{
Factory: func() common.Result { return &bucket.ECInfo{} },
},
}
func Start() Handler {
return WithFallback(
NewHandler(x...),
RawHandler,
func Start() Parser {
return Any(
GraveyardParser,
GarbageParser,
ContainerVolumeParser,
LockedParser,
ShardInfoParser,
PrimaryParser,
LockersParser,
TombstoneParser,
SmallParser,
RootParser,
OwnerParser,
UserAttributeParser,
PayloadHashParser,
ParentParser,
SplitParser,
ContainerCountersParser,
ECInfoParser,
)
}
var (
GraveyardParser = NewPrefixBucketParser(common.Graveyard, GraveyardRecordParser, Resolvers{
cidResolver: LenientResolver,
oidResolver: LenientResolver,
})
GarbageParser = NewPrefixBucketParser(common.Garbage, GarbageRecordParser, Resolvers{
cidResolver: LenientResolver,
oidResolver: LenientResolver,
})
ContainerVolumeParser = NewPrefixBucketParser(common.ContainerVolume, ContainerVolumeRecordParser, Resolvers{
cidResolver: StrictResolver,
oidResolver: StrictResolver,
})
LockedParser = NewPrefixBucketParser(common.Locked, nil, Resolvers{
cidResolver: LenientResolver,
oidResolver: LenientResolver,
})
ShardInfoParser = NewPrefixBucketParser(common.ShardInfo, ShardInfoRecordParser, Resolvers{
cidResolver: StrictResolver,
oidResolver: StrictResolver,
})
PrimaryParser = NewPrefixContainerBucketParser(common.Primary, ObjectRecordParser, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
LockersParser = NewPrefixContainerBucketParser(common.Lockers, ObjectRecordParser, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
TombstoneParser = NewPrefixContainerBucketParser(common.Tombstone, ObjectRecordParser, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
SmallParser = NewPrefixContainerBucketParser(common.Small, nil, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
RootParser = NewPrefixContainerBucketParser(common.Root, nil, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
OwnerParser = NewPrefixContainerBucketParser(common.Owner, nil, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
UserAttributeParser = NewPrefixContainerKeyBucketParser(common.UserAttribute, nil, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
PayloadHashParser = NewPrefixContainerBucketParser(common.PayloadHash, PayloadHashRecordParser, Resolvers{
cidResolver: StrictResolver,
oidResolver: StrictResolver,
})
ParentParser = NewPrefixContainerBucketParser(common.Parent, nil, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
SplitParser = NewPrefixContainerBucketParser(common.Split, nil, Resolvers{
cidResolver: StrictResolver,
oidResolver: StrictResolver,
})
ContainerCountersParser = NewPrefixBucketParser(common.ContainerCounters, nil, Resolvers{
cidResolver: StrictResolver,
oidResolver: StrictResolver,
})
ECInfoParser = NewPrefixContainerBucketParser(common.ECInfo, nil, Resolvers{
cidResolver: StrictResolver,
oidResolver: LenientResolver,
})
)
type (
PrefixBucket struct {
prefix common.Prefix
resolvers Resolvers
}
PrefixContainerBucket struct {
prefix common.Prefix
id cid.ID
resolvers Resolvers
}
PrefixContainerKeyBucket struct {
prefix common.Prefix
id cid.ID
key string
resolvers Resolvers
}
)
type SchemaEntry interface {
String() string
Filter(typ string, val any) FilterResult
}
type Parser func(key, value []byte) (SchemaEntry, Parser, error)
func Any(parsers ...Parser) Parser {
return func(key, value []byte) (SchemaEntry, Parser, error) {
errs := fmt.Errorf("try parse %v %v", key, value)
for _, p := range parsers {
ret, next, err := p(key, value)
if err == nil {
return ret, next, nil
}
errs = errors.Join(errs, err)
}
return nil, nil, fmt.Errorf("no parser succeded: %w", errs)
}
}
type FilterResult int
const (
Yes FilterResult = iota
No
Maybe
)
var (
ErrNotBucket = errors.New("not a bucket")
ErrNotRecord = errors.New("not a record")
ErrInvalidKeyLength = errors.New("invalid key length")
ErrInvalidValueLength = errors.New("invalid value length")
ErrInvalidPrefix = errors.New("invalid prefix")
)
func NewPrefixBucketParser(prefix common.Prefix, next Parser, resolvers Resolvers) Parser {
return func(key, value []byte) (SchemaEntry, Parser, error) {
if value != nil {
return nil, nil, ErrNotBucket
}
if len(key) != 1 {
return nil, nil, ErrInvalidKeyLength
}
var b PrefixBucket
if b.prefix = common.Prefix(key[0]); b.prefix != prefix {
return nil, nil, ErrInvalidPrefix
}
b.resolvers = resolvers
return &b, next, nil
}
}
func NewPrefixContainerBucketParser(prefix common.Prefix, next Parser, resolvers Resolvers) Parser {
return func(key, value []byte) (SchemaEntry, Parser, error) {
if value != nil {
return nil, nil, ErrNotBucket
}
if len(key) != 33 {
return nil, nil, ErrInvalidKeyLength
}
var b PrefixContainerBucket
if b.prefix = common.Prefix(key[0]); b.prefix != prefix {
return nil, nil, ErrInvalidPrefix
}
if err := b.id.Decode(key[1:]); err != nil {
return nil, nil, err
}
b.resolvers = resolvers
return &b, next, nil
}
}
func NewPrefixContainerKeyBucketParser(prefix common.Prefix, next Parser, resolvers Resolvers) Parser {
return func(key, value []byte) (SchemaEntry, Parser, error) {
if value != nil {
return nil, nil, ErrNotBucket
}
if len(key) < 34 {
return nil, nil, ErrInvalidKeyLength
}
var b PrefixContainerKeyBucket
if b.prefix = common.Prefix(key[0]); b.prefix != prefix {
return nil, nil, ErrInvalidPrefix
}
if err := b.id.Decode(key[1:33]); err != nil {
return nil, nil, err
}
b.key = string(key[33:])
b.resolvers = resolvers
return &b, next, nil
}
}
func IfThenElse(condition bool, onSuccess, onFailure FilterResult) FilterResult {
if condition {
return onSuccess
} else {
return onFailure
}
}
type FilterResolver = func(result bool) FilterResult
type Resolvers struct {
cidResolver FilterResolver
oidResolver FilterResolver
}
var (
StrictResolver = func(x bool) FilterResult { return IfThenElse(x, Yes, No) }
LenientResolver = func(x bool) FilterResult { return IfThenElse(x, Yes, Maybe) }
)
func (b *PrefixBucket) String() string {
return b.prefix.String()
}
func (b *PrefixBucket) Filter(typ string, val any) FilterResult {
switch typ {
case "cid":
return b.resolvers.cidResolver(false)
case "oid":
return b.resolvers.oidResolver(false)
default:
return No
}
}
func (b *PrefixContainerBucket) String() string {
return fmt.Sprintf("%s CID %s", b.prefix, b.id)
}
func (b *PrefixContainerBucket) Filter(typ string, val any) FilterResult {
switch typ {
case "cid":
id := val.(cid.ID)
return b.resolvers.cidResolver(b.id.Equals(id))
case "oid":
return b.resolvers.oidResolver(false)
default:
return No
}
}
func (b *PrefixContainerKeyBucket) String() string {
return fmt.Sprintf("%s CID %s %s", b.prefix, b.id, b.key)
}
func (b *PrefixContainerKeyBucket) Filter(typ string, val any) FilterResult {
switch typ {
case "cid":
id := val.(cid.ID)
return b.resolvers.cidResolver(b.id.Equals(id))
case "oid":
return b.resolvers.oidResolver(false)
default:
return No
}
}

View file

@ -0,0 +1,277 @@
package schema
import (
"encoding/binary"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/types/common"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
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"
"github.com/google/uuid"
)
type Record interface {
Decode(key []byte, val []byte) (err error)
}
type (
GraveyardRecord struct {
object, tombstone oid.Address
}
GarbageRecord struct {
addr oid.Address
}
ContainerVolumeRecord struct {
id cid.ID
volume uint64
}
LockedRecord struct {
id oid.ID
ids []oid.ID
}
ShardInfoRecord struct {
label string
value uint64
}
ObjectRecord struct {
id oid.ID
object objectSDK.Object
}
SmallRecord struct {
id oid.ID
storageID *string // optional
}
RootRecord struct {
id oid.ID
info *objectSDK.SplitInfo // optional
}
OwnerRecord struct {
id oid.ID
}
UserAttributeRecord struct {
id oid.ID
}
PayloadHashRecord struct {
checksum checksum.Checksum
ids []oid.ID
}
ParentRecord struct {
parent oid.ID
ids []oid.ID
}
SplitRecord struct {
id uuid.UUID
ids []oid.ID
}
ContainerCountersRecord struct {
logical, physical, user uint64
}
ECInfoRecord struct {
id oid.ID
ids []oid.ID
}
)
func GraveyardRecordParser(key, value []byte) (SchemaEntry, Parser, error) {
if len(key) != 64 {
return nil, nil, ErrInvalidKeyLength
}
if len(value) != 64 {
return nil, nil, ErrInvalidValueLength
}
var (
cnr cid.ID
obj oid.ID
r GraveyardRecord
)
_ = cnr.Decode(key[:32])
_ = obj.Decode(key[32:])
r.object.SetContainer(cnr)
r.object.SetObject(obj)
_ = cnr.Decode(value[:32])
_ = obj.Decode(value[32:])
r.tombstone.SetContainer(cnr)
r.tombstone.SetObject(obj)
return &r, nil, nil
}
func GarbageRecordParser(key, value []byte) (SchemaEntry, Parser, error) {
if len(key) != 64 {
return nil, nil, ErrInvalidKeyLength
}
if len(value) != 0 {
return nil, nil, ErrInvalidValueLength
}
var (
cnr cid.ID
obj oid.ID
r GarbageRecord
)
_ = cnr.Decode(key[:32])
_ = obj.Decode(key[32:])
r.addr.SetContainer(cnr)
r.addr.SetObject(obj)
return &r, nil, nil
}
func ContainerVolumeRecordParser(key, value []byte) (SchemaEntry, Parser, error) {
if len(key) != 32 {
return nil, nil, ErrInvalidKeyLength
}
if len(value) != 8 {
return nil, nil, ErrInvalidValueLength
}
var r ContainerVolumeRecord
_ = r.id.Decode(key)
r.volume = binary.LittleEndian.Uint64(value)
return &r, nil, nil
}
func ShardInfoRecordParser(key, value []byte) (SchemaEntry, Parser, error) {
if len(key) == 0 {
return nil, nil, ErrInvalidKeyLength
}
if len(value) != 8 {
return nil, nil, ErrInvalidValueLength
}
var r ShardInfoRecord
r.label = string(key)
r.value = binary.LittleEndian.Uint64(value)
return &r, nil, nil
}
func ObjectRecordParser(key, value []byte) (SchemaEntry, Parser, error) {
if len(key) != 32 {
return nil, nil, ErrInvalidKeyLength
}
var r ObjectRecord
_ = r.id.Decode(key)
if err := r.object.Unmarshal(value); err != nil {
return nil, nil, err
}
return &r, nil, nil
}
func PayloadHashRecordParser(key, value []byte) (SchemaEntry, Parser, error) {
if len(key) != 32 {
return nil, nil, ErrInvalidKeyLength
}
var (
err error
r PayloadHashRecord
)
r.checksum.SetSHA256([32]byte(key))
if r.ids, err = common.DecodeOIDs(value); err != nil {
return nil, nil, err
}
return &r, nil, nil
}
func (r *GraveyardRecord) String() string {
return fmt.Sprintf("%s %s", r.object, r.tombstone)
}
func (r *GraveyardRecord) Filter(typ string, val any) FilterResult {
switch typ {
case "cid":
id := val.(cid.ID)
return IfThenElse(r.object.Container().Equals(id), Yes, No)
case "oid":
id := val.(oid.ID)
return IfThenElse(r.object.Object().Equals(id), Yes, No)
default:
return No
}
}
func (r *GarbageRecord) String() string {
return r.addr.String()
}
func (r *GarbageRecord) Filter(typ string, val any) FilterResult {
switch typ {
case "cid":
id := val.(cid.ID)
return IfThenElse(r.addr.Container().Equals(id), Yes, No)
case "oid":
id := val.(oid.ID)
return IfThenElse(r.addr.Object().Equals(id), Yes, No)
default:
return No
}
}
func (r *ContainerVolumeRecord) String() string {
return fmt.Sprintf("%s %d", r.id, r.volume)
}
func (r *ContainerVolumeRecord) Filter(typ string, val any) FilterResult {
switch typ {
case "cid":
id := val.(cid.ID)
return IfThenElse(r.id.Equals(id), Yes, No)
default:
return No
}
}
func (r *ShardInfoRecord) String() string {
return fmt.Sprintf("%s %d", r.label, r.value)
}
func (r *ShardInfoRecord) Filter(_ string, _ any) FilterResult {
return No
}
func (r *ObjectRecord) String() string {
return fmt.Sprintf("%s %+v", r.id, r.object)
}
func (r *ObjectRecord) Filter(typ string, val any) FilterResult {
switch typ {
case "oid":
id := val.(oid.ID)
return IfThenElse(r.id.Equals(id), Yes, No)
default:
return No
}
}
func (r *PayloadHashRecord) String() string {
return fmt.Sprintf("%s [...]", r.checksum)
}
func (r *PayloadHashRecord) Filter(_ string, _ any) FilterResult {
return No
}

View file

@ -1,44 +0,0 @@
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

@ -1,75 +0,0 @@
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

@ -1,322 +0,0 @@
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,15 +0,0 @@
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

@ -1,22 +0,0 @@
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

@ -1,12 +0,0 @@
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

@ -1,7 +0,0 @@
package record
import "fmt"
func (r *Object) String() string {
return r.id.String() + " | " + fmt.Sprintf("%v", r.object)
}

View file

@ -1,57 +0,0 @@
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

@ -5,7 +5,6 @@ import (
"fmt"
"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"
@ -21,20 +20,22 @@ import (
// 6E6b24qy32p3L3wjRSUEGNFHMxkUsaqj7udwWVKLzkU
// 1GcnNjFnPof2YbPi2RBi3Sjy3qeR2cymc9BR6i9U2Kt
// 9wynaSKbhrkpQe7knLZvX8XQDW2hk3ZHJnjaLpXsWHtz
// h9MW74svQ8hufDVcaAdyYiaSDZWeTEWo32aqKbTUqAu
type Bucket struct {
name []byte
path [][]byte
nextHandler schema.Handler
result common.Result
nextHandler schema.Parser
result schema.SchemaEntry
}
type Record struct {
key []byte
value []byte
path [][]byte
// nextHandler handlers.Handler
result common.Result
key []byte
value []byte
path [][]byte
result schema.SchemaEntry
}
type Application struct {

View file

@ -10,19 +10,23 @@ import (
"github.com/rivo/tview"
)
func (a *Application) newBucketsView(ctx context.Context, _ *cid.ID, _ *oid.ID) (tview.Primitive, error) {
func (a *Application) newBucketsView(ctx context.Context, cnr *cid.ID, obj *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))
// }
pred := func(schema.SchemaEntry) schema.FilterResult {
return schema.Yes
}
if cnr != nil && obj != nil {
pred = func(x schema.SchemaEntry) schema.FilterResult {
if x.Filter("cid", *cnr) == schema.No {
return schema.No
}
return schema.Yes
}
}
handler := schema.Start()
@ -32,11 +36,7 @@ func (a *Application) newBucketsView(ctx context.Context, _ *cid.ID, _ *oid.ID)
SetExpanded(true).
SetReference(&Bucket{nextHandler: handler})
// if len(filters) == 0 {
err := a.getChildren(ctx, root, true)
// } else {
// err = a.getChildren(ctx, root, false)
// }
_, err := a.getChildren(ctx, root, pred)
if err != nil {
return nil, err
}
@ -51,14 +51,14 @@ func (a *Application) newBucketsView(ctx context.Context, _ *cid.ID, _ *oid.ID)
switch event.Key() {
case tcell.KeyEnter:
if node.IsExpanded() {
node.ClearChildren()
} else {
if len(node.GetChildren()) == 0 {
err := a.getChildren(ctx, node, true)
a.stopOnErr(err)
}
}
// if node.IsExpanded() {
// node.ClearChildren()
// } else {
// if len(node.GetChildren()) == 0 {
// err := a.getChildren(ctx, node, true)
// a.stopOnErr(err)
// }
// }
node.SetExpanded(!node.IsExpanded())
case tcell.KeyTab:
err := a.nav.Push(ctx, func(ctx context.Context) (tview.Primitive, error) {
@ -76,7 +76,11 @@ func (a *Application) newBucketsView(ctx context.Context, _ *cid.ID, _ *oid.ID)
return tree, nil
}
func (a *Application) getChildren(ctx context.Context, parent *tview.TreeNode, lazy bool) error {
func (a *Application) getChildren(
ctx context.Context,
parent *tview.TreeNode,
pred func(schema.SchemaEntry) schema.FilterResult,
) (any, error) {
parentBucket := parent.GetReference().(*Bucket)
path := parentBucket.path
@ -84,60 +88,35 @@ func (a *Application) getChildren(ctx context.Context, parent *tview.TreeNode, l
buffer, err := LoadBuckets(ctx, a.db, path)
if err != nil {
return err
return nil, err
}
for bucket := range buffer {
res, err := handler(bucket.name, nil)
// if errors.Is(err, handlers.ErrFilter) {
// continue
// }
res, next, err := handler(bucket.name, nil)
if err != nil {
continue
// return err
return nil, err
}
bucket.nextHandler = res.Next
bucket.result = res.Result
child := tview.NewTreeNode(res.Result.String())
if pred(res) == schema.No {
continue
}
bucket.nextHandler = next
bucket.result = res
child := tview.NewTreeNode(res.String())
child.SetSelectable(true)
child.SetExpanded(false)
child.SetReference(bucket)
if lazy {
parent.AddChild(child)
continue
}
// _, err = LoadRecords(ctx, a.db, bucket.path)
// if err != nil {
// return nil, err
// }
records, err := LoadRecords(ctx, a.db, bucket.path)
if err != nil {
return err
}
found := false
for record := range records {
_, err = res.Next(record.key, record.value)
// if errors.Is(err, handlers.ErrFilter) {
// continue
// }
if err != nil {
continue
// return err
}
found = true
break
}
err = a.getChildren(ctx, child, lazy)
if err != nil {
return err
}
if found || len(child.GetChildren()) != 0 {
parent.AddChild(child)
}
parent.AddChild(child)
}
return nil
return nil, nil
}

View file

@ -22,7 +22,7 @@ func (a *Application) newRecordsView(ctx context.Context, bkt *Bucket) (tview.Pr
defer close(records)
for record := range temp {
res, err := bkt.nextHandler(record.key, record.value)
res, _, err := bkt.nextHandler(record.key, record.value)
// if errors.Is(err, handlers.ErrFilter) {
// continue
// }
@ -32,7 +32,7 @@ func (a *Application) newRecordsView(ctx context.Context, bkt *Bucket) (tview.Pr
// return
}
// record.nextHandler = next
record.result = res.Result
record.result = res
records <- record
}
}()