github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/doltdb/commit_meta.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package doltdb
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/dolthub/dolt/go/store/types"
    24  )
    25  
    26  const (
    27  	commitMetaNameKey      = "name"
    28  	commitMetaEmailKey     = "email"
    29  	commitMetaDescKey      = "desc"
    30  	commitMetaTimestampKey = "timestamp"
    31  	commitMetaUserTSKey    = "user_timestamp"
    32  	commitMetaVersionKey   = "metaversion"
    33  
    34  	commitMetaStName  = "metadata"
    35  	commitMetaVersion = "1.0"
    36  )
    37  
    38  var ErrNameNotConfigured = errors.New("Aborting commit due to empty committer name. Is your config set?")
    39  var ErrEmailNotConfigured = errors.New("Aborting commit due to empty committer email. Is your config set?")
    40  var ErrEmptyCommitMessage = errors.New("Aborting commit due to empty commit message.")
    41  
    42  var CommitNowFunc = time.Now
    43  var CommitLoc = time.Local
    44  
    45  // CommitMeta contains all the metadata that is associated with a commit within a data repo.
    46  type CommitMeta struct {
    47  	Name          string
    48  	Email         string
    49  	Timestamp     uint64
    50  	Description   string
    51  	UserTimestamp int64
    52  }
    53  
    54  var uMilliToNano = uint64(time.Millisecond / time.Nanosecond)
    55  var milliToNano = int64(time.Millisecond / time.Nanosecond)
    56  var secToMilli = int64(time.Second / time.Millisecond)
    57  
    58  // NewCommitMeta creates a CommitMeta instance from a name, email, and description and uses the current time for the
    59  // timestamp
    60  func NewCommitMeta(name, email, desc string) (*CommitMeta, error) {
    61  	return NewCommitMetaWithUserTS(name, email, desc, CommitNowFunc())
    62  }
    63  
    64  // NewCommitMetaWithUserTS creates a user metadata
    65  func NewCommitMetaWithUserTS(name, email, desc string, userTS time.Time) (*CommitMeta, error) {
    66  	n := strings.TrimSpace(name)
    67  	e := strings.TrimSpace(email)
    68  	d := strings.TrimSpace(desc)
    69  
    70  	if n == "" {
    71  		return nil, ErrNameNotConfigured
    72  	}
    73  
    74  	if e == "" {
    75  		return nil, ErrEmailNotConfigured
    76  	}
    77  
    78  	if d == "" {
    79  		return nil, ErrEmptyCommitMessage
    80  	}
    81  
    82  	ns := uint64(CommitNowFunc().UnixNano())
    83  	ms := ns / uMilliToNano
    84  
    85  	userMS := userTS.UnixNano() / milliToNano
    86  
    87  	return &CommitMeta{n, e, ms, d, userMS}, nil
    88  }
    89  
    90  func getRequiredFromSt(st types.Struct, k string) (types.Value, error) {
    91  	if v, ok, err := st.MaybeGet(k); err != nil {
    92  		return nil, err
    93  	} else if ok {
    94  		return v, nil
    95  	}
    96  
    97  	return nil, errors.New("Missing required field \"" + k + "\".")
    98  }
    99  
   100  func commitMetaFromNomsSt(st types.Struct) (*CommitMeta, error) {
   101  	e, err := getRequiredFromSt(st, commitMetaEmailKey)
   102  
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	n, err := getRequiredFromSt(st, commitMetaNameKey)
   108  
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	d, err := getRequiredFromSt(st, commitMetaDescKey)
   114  
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	ts, err := getRequiredFromSt(st, commitMetaTimestampKey)
   120  
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	userTS, ok, err := st.MaybeGet(commitMetaUserTSKey)
   126  
   127  	if err != nil {
   128  		return nil, err
   129  	} else if !ok {
   130  		userTS = types.Int(int64(uint64(ts.(types.Uint))))
   131  	}
   132  
   133  	return &CommitMeta{
   134  		string(n.(types.String)),
   135  		string(e.(types.String)),
   136  		uint64(ts.(types.Uint)),
   137  		string(d.(types.String)),
   138  		int64(userTS.(types.Int)),
   139  	}, nil
   140  }
   141  
   142  func (cm *CommitMeta) toNomsStruct(nbf *types.NomsBinFormat) (types.Struct, error) {
   143  	metadata := types.StructData{
   144  		commitMetaNameKey:      types.String(cm.Name),
   145  		commitMetaEmailKey:     types.String(cm.Email),
   146  		commitMetaDescKey:      types.String(cm.Description),
   147  		commitMetaTimestampKey: types.Uint(cm.Timestamp),
   148  		commitMetaVersionKey:   types.String(commitMetaVersion),
   149  		commitMetaUserTSKey:    types.Int(cm.UserTimestamp),
   150  	}
   151  
   152  	return types.NewStruct(nbf, commitMetaStName, metadata)
   153  }
   154  
   155  // Time returns the time at which the commit occurred
   156  func (cm *CommitMeta) Time() time.Time {
   157  	seconds := cm.UserTimestamp / secToMilli
   158  	nanos := (cm.UserTimestamp % secToMilli) * milliToNano
   159  	return time.Unix(seconds, nanos)
   160  }
   161  
   162  // FormatTS takes the internal timestamp and turns it into a human readable string in the time.RubyDate format
   163  // which looks like: "Mon Jan 02 15:04:05 -0700 2006"
   164  func (cm *CommitMeta) FormatTS() string {
   165  	return cm.Time().In(CommitLoc).Format(time.RubyDate)
   166  }
   167  
   168  // String returns the human readable string representation of the commit data
   169  func (cm *CommitMeta) String() string {
   170  	return fmt.Sprintf("name: %s, email: %s, timestamp: %s, description: %s", cm.Name, cm.Email, cm.FormatTS(), cm.Description)
   171  }