bare.go

  1package identity
  2
  3import (
  4	"crypto/sha256"
  5	"encoding/json"
  6	"fmt"
  7	"strings"
  8
  9	"github.com/MichaelMure/git-bug/repository"
 10	"github.com/MichaelMure/git-bug/util/lamport"
 11	"github.com/MichaelMure/git-bug/util/text"
 12	"github.com/MichaelMure/git-bug/util/timestamp"
 13)
 14
 15var _ Interface = &Bare{}
 16
 17// Bare is a very minimal identity, designed to be fully embedded directly along
 18// other data.
 19//
 20// in particular, this identity is designed to be compatible with the handling of
 21// identities in the early version of git-bug.
 22type Bare struct {
 23	id        string
 24	name      string
 25	email     string
 26	login     string
 27	avatarUrl string
 28}
 29
 30func NewBare(name string, email string) *Bare {
 31	return &Bare{name: name, email: email}
 32}
 33
 34func NewBareFull(name string, email string, login string, avatarUrl string) *Bare {
 35	return &Bare{name: name, email: email, login: login, avatarUrl: avatarUrl}
 36}
 37
 38type bareIdentityJSON struct {
 39	Name      string `json:"name,omitempty"`
 40	Email     string `json:"email,omitempty"`
 41	Login     string `json:"login,omitempty"`
 42	AvatarUrl string `json:"avatar_url,omitempty"`
 43}
 44
 45func (i *Bare) MarshalJSON() ([]byte, error) {
 46	return json.Marshal(bareIdentityJSON{
 47		Name:      i.name,
 48		Email:     i.email,
 49		Login:     i.login,
 50		AvatarUrl: i.avatarUrl,
 51	})
 52}
 53
 54func (i *Bare) UnmarshalJSON(data []byte) error {
 55	aux := bareIdentityJSON{}
 56
 57	if err := json.Unmarshal(data, &aux); err != nil {
 58		return err
 59	}
 60
 61	i.name = aux.Name
 62	i.email = aux.Email
 63	i.login = aux.Login
 64	i.avatarUrl = aux.AvatarUrl
 65
 66	return nil
 67}
 68
 69// Id return the Identity identifier
 70func (i *Bare) Id() string {
 71	// We don't have a proper ID at hand, so let's hash all the data to get one.
 72	// Hopefully the
 73
 74	if i.id != "" {
 75		return i.id
 76	}
 77
 78	data, err := json.Marshal(i)
 79	if err != nil {
 80		panic(err)
 81	}
 82
 83	h := fmt.Sprintf("%x", sha256.New().Sum(data)[:16])
 84	i.id = string(h)
 85
 86	return i.id
 87}
 88
 89// HumanId return the Identity identifier truncated for human consumption
 90func (i *Bare) HumanId() string {
 91	return FormatHumanID(i.Id())
 92}
 93
 94// Name return the last version of the name
 95func (i *Bare) Name() string {
 96	return i.name
 97}
 98
 99// Email return the last version of the email
100func (i *Bare) Email() string {
101	return i.email
102}
103
104// Login return the last version of the login
105func (i *Bare) Login() string {
106	return i.login
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	switch {
128	case i.name == "" && i.login != "":
129		return i.login
130	case i.name != "" && i.login == "":
131		return i.name
132	case i.name != "" && i.login != "":
133		return fmt.Sprintf("%s (%s)", i.name, i.login)
134	}
135
136	panic("invalid person data")
137}
138
139// Validate check if the Identity data is valid
140func (i *Bare) Validate() error {
141	if text.Empty(i.name) && text.Empty(i.login) {
142		return fmt.Errorf("either name or login should be set")
143	}
144
145	if strings.Contains(i.name, "\n") {
146		return fmt.Errorf("name should be a single line")
147	}
148
149	if !text.Safe(i.name) {
150		return fmt.Errorf("name is not fully printable")
151	}
152
153	if strings.Contains(i.login, "\n") {
154		return fmt.Errorf("login should be a single line")
155	}
156
157	if !text.Safe(i.login) {
158		return fmt.Errorf("login is not fully printable")
159	}
160
161	if strings.Contains(i.email, "\n") {
162		return fmt.Errorf("email should be a single line")
163	}
164
165	if !text.Safe(i.email) {
166		return fmt.Errorf("email is not fully printable")
167	}
168
169	if i.avatarUrl != "" && !text.ValidUrl(i.avatarUrl) {
170		return fmt.Errorf("avatarUrl is not a valid URL")
171	}
172
173	return nil
174}
175
176// Write the identity into the Repository. In particular, this ensure that
177// the Id is properly set.
178func (i *Bare) Commit(repo repository.ClockedRepo) error {
179	// Nothing to do, everything is directly embedded
180	return nil
181}
182
183// If needed, write the identity into the Repository. In particular, this
184// ensure that the Id is properly set.
185func (i *Bare) CommitAsNeeded(repo repository.ClockedRepo) error {
186	// Nothing to do, everything is directly embedded
187	return nil
188}
189
190// IsProtected return true if the chain of git commits started to be signed.
191// If that's the case, only signed commit with a valid key for this identity can be added.
192func (i *Bare) IsProtected() bool {
193	return false
194}
195
196// LastModificationLamportTime return the Lamport time at which the last version of the identity became valid.
197func (i *Bare) LastModificationLamport() lamport.Time {
198	return 0
199}
200
201// LastModification return the timestamp at which the last version of the identity became valid.
202func (i *Bare) LastModification() timestamp.Timestamp {
203	return 0
204}