github.com/ivotron/vio@v0.1.1-0.20160328072646-778e014d4dee/vio.go (about)

     1  package vio
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"gopkg.in/ini.v1"
    12  )
    13  
    14  type Status int
    15  
    16  const (
    17  	Committed Status = iota
    18  	Staged
    19  )
    20  
    21  type version struct {
    22  	revision  string
    23  	timestamp time.Time
    24  	meta      map[string]string
    25  }
    26  
    27  func NewVersion(revision string) *version {
    28  	fields := strings.Split(revision, "#")
    29  
    30  	if len(fields) == 2 {
    31  		i, err := strconv.ParseInt(strings.TrimSpace(fields[1]), 10, 64)
    32  		if err != nil {
    33  			panic(err)
    34  		}
    35  		return &version{
    36  			revision:  fields[0],
    37  			timestamp: time.Unix(i, 0),
    38  			meta:      map[string]string{}}
    39  	} else {
    40  		return &version{
    41  			revision:  fields[0],
    42  			timestamp: time.Now(),
    43  			meta:      map[string]string{}}
    44  	}
    45  }
    46  
    47  func NewVersionWithMeta(revision string, meta map[string]string) *version {
    48  	fields := strings.Split(revision, "#")
    49  
    50  	if len(fields) == 2 {
    51  		i, err := strconv.ParseInt(strings.TrimSpace(fields[1]), 10, 64)
    52  		if err != nil {
    53  			panic(err)
    54  		}
    55  		return &version{
    56  			revision:  fields[0],
    57  			timestamp: time.Unix(i, 0),
    58  			meta:      meta}
    59  	} else {
    60  		return &version{
    61  			revision:  fields[0],
    62  			timestamp: time.Now(),
    63  			meta:      meta}
    64  	}
    65  }
    66  
    67  func ContainsVersion(vs []version, v *version) bool {
    68  	for _, v_in := range vs {
    69  		if v_in.revision == v.revision && v_in.timestamp.Unix() == v.timestamp.Unix() {
    70  			return true
    71  		}
    72  	}
    73  	return false
    74  }
    75  
    76  func (v *version) String() string {
    77  	s, err := json.Marshal(v.meta)
    78  	if err != nil {
    79  		panic(err)
    80  	}
    81  	return fmt.Sprintf("%s#%d,%s", v.revision, v.timestamp.Unix(), s)
    82  }
    83  
    84  type Backend interface {
    85  	// inits backend in current directory
    86  	Init() error
    87  
    88  	// opens the backend
    89  	Open() error
    90  
    91  	// whether the backend has been initialized
    92  	IsInitialized() bool
    93  
    94  	// returns status of backend
    95  	GetStatus() (Status, error)
    96  
    97  	// checks out a commit
    98  	Checkout(v *version) error
    99  
   100  	// commits a version.
   101  	Commit(meta map[string]string) (*version, error)
   102  
   103  	// retrieves the string representation of the diff for a path
   104  	Diff(v1 *version, v2 *version, path string) (string, error)
   105  
   106  	// returns list of committed versions
   107  	GetVersions() (versions []version, err error)
   108  }
   109  
   110  type AnError struct {
   111  	Msg string
   112  }
   113  
   114  func (e AnError) Error() string {
   115  	return e.Msg
   116  }
   117  
   118  func InstantiateBackend(opts *ini.File) (backend Backend, err error) {
   119  	opts.Section("").Key("repo_path").SetValue(".")
   120  	backendType := opts.Section("").Key("backend_type").Value()
   121  	switch backendType {
   122  	case "posix":
   123  		backend, err = NewPosixBackend(opts)
   124  	default:
   125  		return nil, AnError{"unknown backend " + backendType}
   126  	}
   127  	return
   128  }
   129  
   130  func Init(snapsPath string, backend string) (err error) {
   131  	if _, err = os.Stat(".vioconfig"); err == nil {
   132  		return
   133  	}
   134  
   135  	opts := ini.Empty()
   136  	opts.Section("").Key("repo_path").SetValue(".")
   137  	opts.Section("").Key("snapshots_path").SetValue(snapsPath)
   138  	opts.Section("").Key("backend_type").SetValue(backend)
   139  
   140  	b, err := InstantiateBackend(opts)
   141  	if err != nil {
   142  		return
   143  	}
   144  
   145  	err = b.Init()
   146  	if err != nil {
   147  		return
   148  	}
   149  
   150  	opts.Section("").DeleteKey("repo_path")
   151  
   152  	return opts.SaveTo(".vioconfig")
   153  }
   154  
   155  func load() (b Backend, err error) {
   156  	opts, err := ini.Load(".vioconfig")
   157  	if err != nil {
   158  		return
   159  	}
   160  	b, err = InstantiateBackend(opts)
   161  	return
   162  }
   163  
   164  func Commit(message string, meta string) (err error) {
   165  	var t map[string]string
   166  	err = json.Unmarshal([]byte(meta), &t)
   167  	if err != nil {
   168  		return AnError{"Error while unmarshaling JSON: " + err.Error()}
   169  	}
   170  	b, err := load()
   171  	if err != nil {
   172  		return
   173  	}
   174  	t["message"] = message
   175  	_, err = b.Commit(t)
   176  
   177  	return
   178  }
   179  
   180  func Log() (logstr string, err error) {
   181  	b, err := load()
   182  	if err != nil {
   183  		return
   184  	}
   185  	versions, err := b.GetVersions()
   186  	if err != nil {
   187  		return
   188  	}
   189  	for _, v := range versions {
   190  		logstr = logstr +
   191  			fmt.Sprintf("%s#%d %s\n",
   192  				v.revision,
   193  				v.timestamp.Unix(),
   194  				v.meta["message"])
   195  	}
   196  	return
   197  }
   198  
   199  func Checkout(v_str string) (err error) {
   200  	b, err := load()
   201  	if err != nil {
   202  		return
   203  	}
   204  	v := NewVersion(v_str)
   205  	return b.Checkout(v)
   206  }