frostfs-node/cmd/frostfs-lens/schema/parse/parser.go
Aleksey Savchuk 1e07775161
[#1223] lens/tui: Add schema handlers for all bucket types
Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
2024-08-16 11:33:31 +03:00

199 lines
5.4 KiB
Go

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 func(init []byte) (result Result, next []byte, err error)
func Prefix(prefix types.Prefix) Parser {
return func(init []byte) (result Result, next []byte, err error) {
init, found := bytes.CutPrefix(init, []byte{byte(prefix)})
if !found {
return nil, init, fmt.Errorf("expected prefix %d", prefix)
}
return prefix, init, nil
}
}
func CID(init []byte) (result Result, next []byte, err error) {
if len(init) < 32 {
return nil, init, errors.New("can't parse CID, not enough data")
}
id := new(cid.ID)
err = id.Decode(init[:32])
if err != nil {
return nil, init, fmt.Errorf("can't parse CID: %w", err)
}
return &types.CID{ID: *id}, init[32:], nil
}
func OID(init []byte) (result Result, next []byte, err error) {
if len(init) < 32 {
return nil, init, errors.New("can't parse OID, not enough data")
}
id := new(oid.ID)
err = id.Decode(init[:32])
if err != nil {
return nil, init, fmt.Errorf("can't parse OID: %w", err)
}
return &types.OID{ID: *id}, init[32:], nil
}
func UID(init []byte) (result Result, next []byte, err error) {
id := new(user.ID)
if err = id.DecodeString(string(init)); err != nil {
return nil, init, fmt.Errorf("can't parse UID: %w", err)
}
return &types.UID{ID: *id}, nil, nil
}
func Checksum(init []byte) (result Result, next []byte, err error) {
if len(init) < 32 {
return nil, init, errors.New("can't parse checksum, not enough data")
}
return &types.Checksum{Value: init[:32]}, init[32:], nil
}
func Object(init []byte) (result Result, next []byte, err error) {
obj := objectSDK.New()
if err = obj.Unmarshal(init); err != nil {
return nil, init, fmt.Errorf("can't parse object: %w", err)
}
return &types.Object{Obj: obj}, nil, nil
}
func SplitInfo(init []byte) (result Result, next []byte, err error) {
info := objectSDK.NewSplitInfo()
if err = info.Unmarshal(init); err != nil {
return nil, init, fmt.Errorf("can't parse split info: %w", err)
}
return &types.SplitInfo{Info: info}, nil, nil
}
func StorageID(init []byte) (result Result, next []byte, err error) {
if len(init) == 0 {
return nil, init, errors.New("can't parse storage id, unexpected EOF")
}
return &resultFromString{string(init)}, nil, nil
}
func Attribute(init []byte) (result Result, next []byte, err error) {
if len(init) == 0 {
return nil, init, errors.New("can't parse attribute, unexpected EOF")
}
return &resultFromString{string(init)}, nil, nil
}
func OIDsList(init []byte) (result Result, next []byte, err error) {
r := io.NewBinReaderFromBuf(init)
size := r.ReadVarUint()
oids := make([]Result, size)
var i uint64
for ; i < size; i++ {
id := new(oid.ID)
if err = id.Decode(r.ReadVarBytes()); err != nil {
return nil, init, fmt.Errorf("can't parse list of OIDs: %w", err)
}
oids[i] = types.OID{ID: *id}
}
return &resultUnion{oids}, nil, nil
}
func ObjectCounters(init []byte) (result Result, next []byte, err error) {
counters := &types.ObjectCounters{}
next, err = counters.Decode(init)
if err != nil {
return nil, init, fmt.Errorf("can't parse object counters: %w", err)
}
return counters, next, nil
}
func UUID(init []byte) (result Result, next []byte, err error) {
if len(init) < 16 {
return nil, init, errors.New("can't parse UUID, not enough data")
}
id := uuid.UUID{}
if err := id.UnmarshalBinary(init[:16]); err != nil {
return nil, init, fmt.Errorf("can't parsse UUID: %w", err)
}
return id, init[16:], nil
}
func Volume(init []byte) (result Result, next []byte, err error) {
if len(init) < 8 {
return nil, init, errors.New("can't parse volume, not enough data")
}
volume := binary.LittleEndian.Uint64(init[:8])
return &types.Volume{Volume: volume}, init[8:], nil
}
func ExactString(s string) Parser {
return func(init []byte) (result Result, next []byte, err error) {
next, found := bytes.CutPrefix(init, []byte(s))
if !found {
return nil, init, errors.New("can't parse string")
}
return &resultFromString{str: s}, next, nil
}
}
func Skip(_ []byte) (result Result, next []byte, err error) {
return &resultFromString{}, nil, nil
}
func EOF(init []byte) (result Result, next []byte, err error) {
if len(init) != 0 {
return nil, init, fmt.Errorf("expected EOF but got %v", init)
}
return &resultFromString{}, nil, err
}
func Any(parsers ...Parser) Parser {
return func(init []byte) (result Result, next []byte, err error) {
var errs error
for _, parser := range parsers {
result, next, err = parser(init)
if err == nil {
return result, next, nil
}
errs = errors.Join(errs, err)
}
return nil, init, errs
}
}
func Chain(parsers ...Parser) Parser {
return func(init []byte) (result Result, next []byte, err error) {
var results []Result
current := init
for _, parser := range parsers {
result, current, err = parser(current)
if err != nil {
return nil, init, err
}
results = append(results, result)
}
return &resultUnion{results}, current, nil
}
}
func ChainWithEOF(parsers ...Parser) Parser {
return Chain(Chain(parsers...), EOF)
}