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