199 lines
5.4 KiB
Go
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)
|
|
}
|