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.
 24// Deprecated: legacy identity for compat, might make sense to ditch entirely for
 25// simplicity but that would be a breaking change.
 26type Bare struct {
 27	id        entity.Id
 28	name      string
 29	email     string
 30	login     string
 31	avatarUrl string
 32}
 33
 34func NewBare(name string, email string) *Bare {
 35	return &Bare{id: entity.UnsetId, name: name, email: email}
 36}
 37
 38func NewBareFull(name string, email string, login string, avatarUrl string) *Bare {
 39	return &Bare{id: entity.UnsetId, name: name, email: email, login: login, avatarUrl: avatarUrl}
 40}
 41
 42func deriveId(data []byte) entity.Id {
 43	sum := sha256.Sum256(data)
 44	return entity.Id(fmt.Sprintf("%x", sum))
 45}
 46
 47type bareIdentityJSON struct {
 48	Name      string `json:"name,omitempty"`
 49	Email     string `json:"email,omitempty"`
 50	Login     string `json:"login,omitempty"`
 51	AvatarUrl string `json:"avatar_url,omitempty"`
 52}
 53
 54func (i *Bare) MarshalJSON() ([]byte, error) {
 55	return json.Marshal(bareIdentityJSON{
 56		Name:      i.name,
 57		Email:     i.email,
 58		Login:     i.login,
 59		AvatarUrl: i.avatarUrl,
 60	})
 61}
 62
 63func (i *Bare) UnmarshalJSON(data []byte) error {
 64	// Compute the Id when loading the op from disk.
 65	i.id = deriveId(data)
 66
 67	aux := bareIdentityJSON{}
 68
 69	if err := json.Unmarshal(data, &aux); err != nil {
 70		return err
 71	}
 72
 73	i.name = aux.Name
 74	i.email = aux.Email
 75	i.login = aux.Login
 76	i.avatarUrl = aux.AvatarUrl
 77
 78	return nil
 79}
 80
 81// Id return the Identity identifier
 82func (i *Bare) Id() entity.Id {
 83	// We don't have a proper Id at hand, so let's hash all the data to get one.
 84
 85	if i.id == "" {
 86		// something went really wrong
 87		panic("identity's id not set")
 88	}
 89	if i.id == entity.UnsetId {
 90		// This means we are trying to get the identity identifier *before* it has been stored
 91		// As the Id is computed based on the actual bytes written on the disk, we are going to predict
 92		// those and then get the Id. This is safe as it will be the exact same code writing on disk later.
 93
 94		data, err := json.Marshal(i)
 95		if err != nil {
 96			panic(err)
 97		}
 98
 99		i.id = deriveId(data)
100	}
101	return i.id
102}
103
104// Name return the last version of the name
105func (i *Bare) Name() string {
106	return i.name
107}
108
109// Email return the last version of the email
110func (i *Bare) Email() string {
111	return i.email
112}
113
114// Login return the last version of the login
115func (i *Bare) Login() string {
116	return i.login
117}
118
119// AvatarUrl return the last version of the Avatar URL
120func (i *Bare) AvatarUrl() string {
121	return i.avatarUrl
122}
123
124// Keys return the last version of the valid keys
125func (i *Bare) Keys() []*Key {
126	return nil
127}
128
129// ValidKeysAtTime return the set of keys valid at a given lamport time
130func (i *Bare) ValidKeysAtTime(_ lamport.Time) []*Key {
131	return nil
132}
133
134// DisplayName return a non-empty string to display, representing the
135// identity, based on the non-empty values.
136func (i *Bare) DisplayName() string {
137	switch {
138	case i.name == "" && i.login != "":
139		return i.login
140	case i.name != "" && i.login == "":
141		return i.name
142	case i.name != "" && i.login != "":
143		return fmt.Sprintf("%s (%s)", i.name, i.login)
144	}
145
146	panic("invalid person data")
147}
148
149// Validate check if the Identity data is valid
150func (i *Bare) Validate() error {
151	if text.Empty(i.name) && text.Empty(i.login) {
152		return fmt.Errorf("either name or login should be set")
153	}
154
155	if strings.Contains(i.name, "\n") {
156		return fmt.Errorf("name should be a single line")
157	}
158
159	if !text.Safe(i.name) {
160		return fmt.Errorf("name is not fully printable")
161	}
162
163	if strings.Contains(i.login, "\n") {
164		return fmt.Errorf("login should be a single line")
165	}
166
167	if !text.Safe(i.login) {
168		return fmt.Errorf("login is not fully printable")
169	}
170
171	if strings.Contains(i.email, "\n") {
172		return fmt.Errorf("email should be a single line")
173	}
174
175	if !text.Safe(i.email) {
176		return fmt.Errorf("email is not fully printable")
177	}
178
179	if i.avatarUrl != "" && !text.ValidUrl(i.avatarUrl) {
180		return fmt.Errorf("avatarUrl is not a valid URL")
181	}
182
183	return nil
184}
185
186// Write the identity into the Repository. In particular, this ensure that
187// the Id is properly set.
188func (i *Bare) CommitWithRepo(repo repository.ClockedRepo) error {
189	// Nothing to do, everything is directly embedded
190	return nil
191}
192
193// If needed, write the identity into the Repository. In particular, this
194// ensure that the Id is properly set.
195func (i *Bare) CommitAsNeededWithRepo(repo repository.ClockedRepo) error {
196	// Nothing to do, everything is directly embedded
197	return nil
198}
199
200// IsProtected return true if the chain of git commits started to be signed.
201// If that's the case, only signed commit with a valid key for this identity can be added.
202func (i *Bare) IsProtected() bool {
203	return false
204}
205
206// LastModificationLamportTime return the Lamport time at which the last version of the identity became valid.
207func (i *Bare) LastModificationLamport() lamport.Time {
208	return 0
209}
210
211// LastModification return the timestamp at which the last version of the identity became valid.
212func (i *Bare) LastModification() timestamp.Timestamp {
213	return 0
214}
215
216func (i *Bare) NeedCommit() bool {
217	return false
218}