github.com/rcowham/go-libgitfastimport@v0.1.6/types.go (about)

     1  // Copyright (C) 2017-2018, 2020-2021  Luke Shumaker <lukeshu@lukeshu.com>
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  
    16  package libfastimport
    17  
    18  import (
    19  	"fmt"
    20  	"strconv"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  // Ident is a tuple of a commiter's (or author's) name, email, and a
    28  // timestamp with timezone.
    29  //
    30  // BUG(lukeshu): Ident (and ParseIdent) only supports the
    31  // "raw"/"raw-permissive" date format (not "rfc2822" or "now")
    32  type Ident struct {
    33  	Name  string
    34  	Email string
    35  	Time  time.Time
    36  }
    37  
    38  func (ut Ident) String() string {
    39  	if ut.Name == "" {
    40  		return fmt.Sprintf("<%s> %d %s",
    41  			ut.Email,
    42  			ut.Time.Unix(),
    43  			ut.Time.Format("-0700"))
    44  	} else {
    45  		return fmt.Sprintf("%s <%s> %d %s",
    46  			ut.Name,
    47  			ut.Email,
    48  			ut.Time.Unix(),
    49  			ut.Time.Format("-0700"))
    50  	}
    51  }
    52  
    53  // ParseIdent parses a string containing an Ident.
    54  //
    55  // The format of this string is
    56  //
    57  //     <name> SP LT <email> GT SP <time> SP <offutc>
    58  //
    59  // Where <name> may contain a space, but not "<" or ">"; <time> is an
    60  // integer number of seconds since the UNIX epoch (UTC); <offutc> is
    61  // positive or negative 4-digit offset from UTC (for example, EST
    62  // would be "-0500").
    63  func ParseIdent(str string) (Ident, error) {
    64  	ret := Ident{}
    65  	lt := strings.IndexAny(str, "<>")
    66  	if lt < 0 || str[lt] != '<' {
    67  		return ret, errors.Errorf("Missing < in ident string: %q", str)
    68  	}
    69  	if lt > 0 {
    70  		if str[lt-1] != ' ' {
    71  			return ret, errors.Errorf("Missing space before < in ident string: %q", str)
    72  		}
    73  		ret.Name = str[:lt-1]
    74  	}
    75  	gt := lt + 1 + strings.IndexAny(str[lt+1:], "<>")
    76  	if gt < lt+1 || str[gt] != '>' {
    77  		return ret, errors.Errorf("Missing > in ident string: %q", str)
    78  	}
    79  	if str[gt+1] != ' ' {
    80  		return ret, errors.Errorf("Missing space after > in ident string: %q", str)
    81  	}
    82  	ret.Email = str[lt+1 : gt]
    83  
    84  	strWhen := str[gt+2:]
    85  	sp := strings.IndexByte(strWhen, ' ')
    86  	if sp < 0 {
    87  		return ret, errors.Errorf("missing time zone in when: %q", str)
    88  	}
    89  	sec, err := strconv.ParseInt(strWhen[:sp], 10, 64)
    90  	if err != nil {
    91  		return ret, err
    92  	}
    93  	tzt, err := time.Parse("-0700", strWhen[sp+1:])
    94  	if err != nil {
    95  		return ret, err
    96  	}
    97  	ret.Time = time.Unix(sec, 0).In(tzt.Location())
    98  
    99  	return ret, nil
   100  }
   101  
   102  // Mode is a file mode as seen by git.
   103  type Mode uint32 // 18 bits
   104  
   105  const (
   106  	ModeFil = Mode(0100644) // A regular file
   107  	ModeExe = Mode(0100755) // An executable file
   108  	ModeSym = Mode(0120000) // A symbolic link
   109  	ModeGit = Mode(0160000) // A nested git repository (e.g. submodule)
   110  	ModeDir = Mode(0040000) // A directory
   111  )
   112  
   113  func (m Mode) String() string {
   114  	return fmt.Sprintf("%06o", m)
   115  }
   116  
   117  func (m Mode) GoString() string {
   118  	return fmt.Sprintf("%07o", m)
   119  }
   120  
   121  // Path is a string storing a git path.
   122  type Path string
   123  
   124  // PathEscape escapes a path in case it contains special characters.
   125  func PathEscape(path Path) string {
   126  	if strings.HasPrefix(string(path), "\"") || strings.ContainsRune(string(path), ' ') || strings.ContainsRune(string(path), '\n') {
   127  		return "\"" + strings.Replace(strings.Replace(strings.Replace(string(path), "\\", "\\\\", -1), "\"", "\\\"", -1), "\n", "\\n", -1) + "\""
   128  	} else {
   129  		return string(path)
   130  	}
   131  }
   132  
   133  // PathUnescape unescapes a quoted path.
   134  func PathUnescape(epath string) Path {
   135  	if strings.HasPrefix(epath, "\"") {
   136  		return Path(strings.Replace(strings.Replace(strings.Replace(epath[1:len(epath)-1], "\\n", "\n", -1), "\\\"", "\"", -1), "\\\\", "\\", -1))
   137  	} else {
   138  		return Path(epath)
   139  	}
   140  }
   141  
   142  // String calls PathEscape on the Path.
   143  func (p Path) String() string {
   144  	return PathEscape(p)
   145  }