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 }