bare.go

  1package identity
  2
  3import (
  4	"crypto/sha256"
  5	"encoding/json"
  6	"fmt"
  7	"strings"
  8
  9	"github.com/MichaelMure/git-bug/entity"
 10	"github.com/MichaelMure/git-bug/repository"
 11	"github.com/MichaelMure/git-bug/util/lamport"
 12	"github.com/MichaelMure/git-bug/util/text"
 13	"github.com/MichaelMure/git-bug/util/timestamp"
 14)
 15
 16var _ Interface = &Bare{}
 17var _ entity.Interface = &Bare{}
 18
 19// Bare is a very minimal identity, designed to be fully embedded directly along
 20// other data.
 21//
 22// in particular, this identity is designed to be compatible with the handling of
 23// identities in the early version of git-bug.
 24type Bare struct {
 25	id        entity.Id
 26	name      string
 27	email     string
 28	avatarUrl string
 29}
 30
 31func NewBare(name string, email string) *Bare {
 32	return &Bare{id: entity.UnsetId, name: name, email: email}
 33}
 34
 35func NewBareFull(name string, email string, avatarUrl string) *Bare {
 36	return &Bare{id: entity.UnsetId, name: name, email: email, avatarUrl: avatarUrl}
 37}
 38
 39func deriveId(data []byte) entity.Id {
 40	sum := sha256.Sum256(data)
 41	return entity.Id(fmt.Sprintf("%x", sum))
 42}
 43
 44type bareIdentityJSON struct {
 45	Name      string `json:"name,omitempty"`
 46	Email     string `json:"email,omitempty"`
 47	Login     string `json:"login,omitempty"` // Deprecated, only kept to have the same ID when reading an old value
 48	AvatarUrl string `json:"avatar_url,omitempty"`
 49}
 50
 51func (i *Bare) MarshalJSON() ([]byte, error) {
 52	return json.Marshal(bareIdentityJSON{
 53		Name:      i.name,
 54		Email:     i.email,
 55		AvatarUrl: i.avatarUrl,
 56	})
 57}
 58
 59func (i *Bare) UnmarshalJSON(data []byte) error {
 60	// Compute the Id when loading the op from disk.
 61	i.id = deriveId(data)
 62
 63	aux := bareIdentityJSON{}
 64
 65	if err := json.Unmarshal(data, &aux); err != nil {
 66		return err
 67	}
 68
 69	i.name = aux.Name
 70	i.email = aux.Email
 71	i.avatarUrl = aux.AvatarUrl
 72
 73	return nil
 74}
 75
 76// Id return the Identity identifier
 77func (i *Bare) Id() entity.Id {
 78	// We don't have a proper Id at hand, so let's hash all the data to get one.
 79
 80	if i.id == "" {
 81		// something went really wrong
 82		panic("identity's id not set")
 83	}
 84	if i.id == entity.UnsetId {
 85		// This means we are trying to get the identity identifier *before* it has been stored
 86		// As the Id is computed based on the actual bytes written on the disk, we are going to predict
 87		// those and then get the Id. This is safe as it will be the exact same code writing on disk later.
 88
 89		data, err := json.Marshal(i)
 90		if err != nil {
 91			panic(err)
 92		}
 93
 94		i.id = deriveId(data)
 95	}
 96	return i.id
 97}
 98
 99// Name return the last version of the name
100func (i *Bare) Name() string {
101	return i.name
102}
103
104// Email return the last version of the email
105func (i *Bare) Email() string {
106	return i.email
107}
108
109// AvatarUrl return the last version of the Avatar URL
110func (i *Bare) AvatarUrl() string {
111	return i.avatarUrl
112}
113
114// Keys return the last version of the valid keys
115func (i *Bare) Keys() []Key {
116	return []Key{}
117}
118
119// ValidKeysAtTime return the set of keys valid at a given lamport time
120func (i *Bare) ValidKeysAtTime(time lamport.Time) []Key {
121	return []Key{}
122}
123
124// DisplayName return a non-empty string to display, representing the
125// identity, based on the non-empty values.
126func (i *Bare) DisplayName() string {
127	return i.name
128}
129
130// Validate check if the Identity data is valid
131func (i *Bare) Validate() error {
132	if text.Empty(i.name) {
133		return fmt.Errorf("name is not set")
134	}
135
136	if strings.Contains(i.name, "\n") {
137		return fmt.Errorf("name should be a single line")
138	}
139
140	if !text.Safe(i.name) {
141		return fmt.Errorf("name is not fully printable")
142	}
143
144	if strings.Contains(i.email, "\n") {
145		return fmt.Errorf("email should be a single line")
146	}
147
148	if !text.Safe(i.email) {
149		return fmt.Errorf("email is not fully printable")
150	}
151
152	if i.avatarUrl != "" && !text.ValidUrl(i.avatarUrl) {
153		return fmt.Errorf("avatarUrl is not a valid URL")
154	}
155
156	return nil
157}
158
159// Write the identity into the Repository. In particular, this ensure that
160// the Id is properly set.
161func (i *Bare) Commit(repo repository.ClockedRepo) error {
162	// Nothing to do, everything is directly embedded
163	return nil
164}
165
166// If needed, write the identity into the Repository. In particular, this
167// ensure that the Id is properly set.
168func (i *Bare) CommitAsNeeded(repo repository.ClockedRepo) error {
169	// Nothing to do, everything is directly embedded
170	return nil
171}
172
173// IsProtected return true if the chain of git commits started to be signed.
174// If that's the case, only signed commit with a valid key for this identity can be added.
175func (i *Bare) IsProtected() bool {
176	return false
177}
178
179// LastModificationLamportTime return the Lamport time at which the last version of the identity became valid.
180func (i *Bare) LastModificationLamport() lamport.Time {
181	return 0
182}
183
184// LastModification return the timestamp at which the last version of the identity became valid.
185func (i *Bare) LastModification() timestamp.Timestamp {
186	return 0
187}