1// Copyright 2016 Keybase Inc. All rights reserved.
2// Use of this source code is governed by a BSD
3// license that can be found in the LICENSE file.
4
5package kbfsmd
6
7import (
8	"encoding"
9	"encoding/hex"
10
11	"github.com/keybase/client/go/kbfs/kbfscodec"
12	"github.com/keybase/client/go/kbfs/kbfshash"
13	"github.com/pkg/errors"
14)
15
16// ID is the content-based ID for a metadata block.
17type ID struct {
18	h kbfshash.Hash
19}
20
21var _ encoding.BinaryMarshaler = ID{}
22var _ encoding.BinaryUnmarshaler = (*ID)(nil)
23
24var _ encoding.TextMarshaler = ID{}
25var _ encoding.TextUnmarshaler = (*ID)(nil)
26
27// MakeID creates a new ID from the given RootMetadata object.
28func MakeID(codec kbfscodec.Codec, md RootMetadata) (ID, error) {
29	// Make sure that the serialized metadata is set; otherwise we
30	// won't get the right ID.
31	if md.GetSerializedPrivateMetadata() == nil {
32		return ID{}, errors.WithStack(MissingDataError{md.TlfID()})
33	}
34
35	buf, err := codec.Encode(md)
36	if err != nil {
37		return ID{}, err
38	}
39
40	h, err := kbfshash.DefaultHash(buf)
41	if err != nil {
42		return ID{}, err
43	}
44
45	return ID{h}, nil
46}
47
48// FakeID returns an ID derived from the given byte, suitable for
49// testing.
50func FakeID(b byte) ID {
51	dh := kbfshash.RawDefaultHash{b}
52	h, err := kbfshash.HashFromRaw(kbfshash.DefaultHashType, dh[:])
53	if err != nil {
54		panic(err)
55	}
56	return ID{h}
57}
58
59// Bytes returns the bytes of the MDID.
60func (id ID) Bytes() []byte {
61	return id.h.Bytes()
62}
63
64func (id ID) String() string {
65	return id.h.String()
66}
67
68// MarshalBinary implements the encoding.BinaryMarshaler interface for
69// ID. Returns an error if the ID is invalid and not the zero
70// ID.
71func (id ID) MarshalBinary() (data []byte, err error) {
72	return id.h.MarshalBinary()
73}
74
75// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
76// for ID. Returns an error if the given byte array is non-empty and
77// the ID is invalid.
78func (id *ID) UnmarshalBinary(data []byte) error {
79	return id.h.UnmarshalBinary(data)
80}
81
82// MarshalText implements the encoding.TextMarshaler interface for ID.
83func (id ID) MarshalText() ([]byte, error) {
84	bytes, err := id.MarshalBinary()
85	if err != nil {
86		return nil, err
87	}
88	return []byte(hex.EncodeToString(bytes)), nil
89}
90
91// UnmarshalText implements the encoding.TextUnmarshaler interface for
92// ID.
93func (id *ID) UnmarshalText(buf []byte) error {
94	s := string(buf)
95	bytes, err := hex.DecodeString(s)
96	if err != nil {
97		return errors.WithStack(InvalidIDError{s})
98	}
99	return id.UnmarshalBinary(bytes)
100}
101
102// IsValid returns whether the ID is valid.
103func (id ID) IsValid() bool {
104	return id.h.IsValid()
105}
106
107// ParseID parses a hex encoded ID. Returns ID{} and an InvalidIDError
108// on failure.
109func ParseID(s string) (ID, error) {
110	var id ID
111	err := id.UnmarshalText([]byte(s))
112	if err != nil {
113		return ID{}, err
114	}
115	return id, nil
116}
117