shithub: furgit

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

View raw version
package commit

import (
	"bytes"
	"errors"
	"fmt"

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

// Parse decodes a commit object body.
func Parse(body []byte, algo objectid.Algorithm) (*Commit, error) {
	c := new(Commit)

	i := 0
	for i < len(body) {
		rel := bytes.IndexByte(body[i:], '\n')
		if rel < 0 {
			return nil, errors.New("object: commit: 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: commit: malformed header")
		}

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

			c.Tree = id
		case "parent":
			id, err := objectid.ParseHex(algo, string(value))
			if err != nil {
				return nil, fmt.Errorf("object: commit: parent: %w", err)
			}

			c.Parents = append(c.Parents, id)
		case "author":
			idt, err := objectsignature.Parse(value)
			if err != nil {
				return nil, fmt.Errorf("object: commit: author: %w", err)
			}

			c.Author = *idt
		case "committer":
			idt, err := objectsignature.Parse(value)
			if err != nil {
				return nil, fmt.Errorf("object: commit: committer: %w", err)
			}

			c.Committer = *idt
		case "change-id":
			c.ChangeID = string(value)
		case "gpgsig", "gpgsig-sha256":
			for i < len(body) {
				nextRel := bytes.IndexByte(body[i:], '\n')
				if nextRel < 0 {
					return nil, errors.New("object: commit: unterminated gpgsig")
				}

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

				i += nextRel + 1
			}
		default:
			c.ExtraHeaders = append(c.ExtraHeaders, ExtraHeader{
				Key:   string(key),
				Value: append([]byte(nil), value...),
			})
		}
	}

	if i > len(body) {
		return nil, errors.New("object: commit: parser position out of bounds")
	}

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

	return c, nil
}