shithub: furgit

ref: df1f2fb3daa1acd25c88510f259d5535fb482126
dir: /object/tag/parse.go/

View raw version
package tag

import (
	"bytes"
	"errors"
	"fmt"

	objectid "codeberg.org/lindenii/furgit/object/id"
	objectsignature "codeberg.org/lindenii/furgit/object/signature"
	objecttype "codeberg.org/lindenii/furgit/object/type"
)

// Parse decodes a tag object body.
func Parse(body []byte, algo objectid.Algorithm) (*Tag, error) {
	t := new(Tag)
	i := 0

	var haveTarget, haveType bool

	for i < len(body) {
		rel := bytes.IndexByte(body[i:], '\n')
		if rel < 0 {
			return nil, errors.New("object: tag: missing newline")
		}

		line := body[i : i+rel]
		i += rel + 1

		if len(line) == 0 {
			break
		}

		key, value, found := bytes.Cut(line, []byte{' '})
		if !found {
			return nil, errors.New("object: tag: malformed header")
		}

		switch string(key) {
		case "object":
			id, err := objectid.ParseHex(algo, string(value))
			if err != nil {
				return nil, fmt.Errorf("object: tag: object: %w", err)
			}

			t.Target = id
			haveTarget = true
		case "type":
			ty, ok := objecttype.Parse(string(value))
			if !ok {
				return nil, errors.New("object: tag: unknown target type")
			}

			t.TargetType = ty
			haveType = true
		case "tag":
			t.Name = append([]byte(nil), value...)
		case "tagger":
			idt, err := objectsignature.Parse(value)
			if err != nil {
				return nil, fmt.Errorf("object: tag: tagger: %w", err)
			}

			t.Tagger = idt
		case "gpgsig", "gpgsig-sha256":
			for i < len(body) {
				nextRel := bytes.IndexByte(body[i:], '\n')
				if nextRel < 0 {
					return nil, errors.New("object: tag: unterminated gpgsig")
				}

				if body[i] != ' ' {
					break
				}

				i += nextRel + 1
			}
		default:
			// Ignore unknown headers for now.
		}
	}

	if !haveTarget || !haveType {
		return nil, errors.New("object: tag: missing required headers")
	}

	t.Message = append([]byte(nil), body[i:]...)

	return t, nil
}