]> git.lizzy.rs Git - micro.git/blob - plugin.go
5913e3785a7f7d7c2ae28d7b4792024dbf6a440b
[micro.git] / plugin.go
1 package manager
2
3 import (
4         "bytes"
5         "encoding/json"
6         "errors"
7         "io/ioutil"
8         "path"
9
10         "github.com/blang/semver"
11         "github.com/zyedidia/micro/internal/config"
12         git "gopkg.in/src-d/go-git.v4"
13 )
14
15 var (
16         ErrMissingName     = errors.New("Missing or empty name field")
17         ErrMissingDesc     = errors.New("Missing or empty description field")
18         ErrMissingSite     = errors.New("Missing or empty website field")
19         ErrMissingRepo     = errors.New("Missing or empty repository field")
20         ErrMissingVersions = errors.New("Missing or empty versions field")
21         ErrMissingTag      = errors.New("Missing or empty tag field")
22         ErrMissingRequire  = errors.New("Missing or empty require field")
23 )
24
25 const (
26         infojson    = "plugin.json"
27         versionfile = "version.lock"
28 )
29
30 type Plugin struct {
31         info    *PluginInfo
32         dir     string
33         repo    *git.Repository
34         version semver.Version // currently installed version
35 }
36
37 func (p *Plugin) GetRequires() *PluginVersion {
38         for _, v := range p.info.Versions {
39                 if p.version.Equals(v.Vers) {
40                         return &v
41                 }
42         }
43         return nil
44 }
45
46 // PluginVersion describes a version for a plugin as well as any dependencies that
47 // version might have
48 // This marks a tag that corresponds to the version in the git repo
49 type PluginVersion struct {
50         Vers    semver.Version
51         Vstr    string            `json:"version"`
52         Tag     string            `json:"tag"`
53         Require map[string]string `json:"require"`
54 }
55
56 // PluginInfo contains all the needed info about a plugin
57 type PluginInfo struct {
58         Name     string          `json:"name"`
59         Desc     string          `json:"description"`
60         Site     string          `json:"website"`
61         Repo     string          `json:"repository"`
62         Versions []PluginVersion `json:"versions"`
63 }
64
65 // NewPluginInfo parses a JSON input into a valid PluginInfo struct
66 // Returns an error if there are any missing fields or any invalid fields
67 // There are no optional fields in a plugin info json file
68 func NewPluginInfo(data []byte) (*PluginInfo, error) {
69         var info PluginInfo
70
71         dec := json.NewDecoder(bytes.NewReader(data))
72         dec.DisallowUnknownFields() // Force errors
73
74         if err := dec.Decode(&info); err != nil {
75                 return nil, err
76         }
77
78         if len(info.Name) == 0 {
79                 return nil, ErrMissingName
80         } else if len(info.Desc) == 0 {
81                 return nil, ErrMissingDesc
82         } else if len(info.Site) == 0 {
83                 return nil, ErrMissingSite
84         } else if len(info.Repo) == 0 {
85                 return nil, ErrMissingRepo
86         } else if err := info.makeVersions(); err != nil {
87                 return nil, err
88         }
89
90         return &info, nil
91 }
92
93 func (i *PluginInfo) makeVersions() error {
94         if len(i.Versions) == 0 {
95                 return ErrMissingVersions
96         }
97
98         for _, v := range i.Versions {
99                 sv, err := semver.Make(v.Vstr)
100                 if err != nil {
101                         return err
102                 }
103                 v.Vers = sv
104                 if len(v.Tag) == 0 {
105                         return ErrMissingTag
106                 } else if v.Require == nil {
107                         return ErrMissingRequire
108                 }
109         }
110
111         return nil
112 }
113
114 // ListInstalledPlugins searches the config directory for all installed plugins
115 // and returns the list of plugin infos corresponding to them
116 func ListInstalledPlugins() ([]*Plugin, error) {
117         pdir := path.Join(config.ConfigDir, "plugin")
118
119         files, err := ioutil.ReadDir(pdir)
120         if err != nil {
121                 return nil, err
122         }
123
124         var plugins []*Plugin
125
126         for _, dir := range files {
127                 if dir.IsDir() {
128                         files, err := ioutil.ReadDir(path.Join(pdir, dir.Name()))
129                         if err != nil {
130                                 return nil, err
131                         }
132
133                         for _, f := range files {
134                                 if f.Name() == infojson {
135                                         dat, err := ioutil.ReadFile(path.Join(pdir, dir.Name(), infojson))
136                                         if err != nil {
137                                                 return nil, err
138                                         }
139                                         info, err := NewPluginInfo(dat)
140                                         if err != nil {
141                                                 return nil, err
142                                         }
143
144                                         versiondat, err := ioutil.ReadFile(path.Join(pdir, dir.Name(), versionfile))
145                                         if err != nil {
146                                                 return nil, err
147                                         }
148                                         sv, err := semver.Make(string(versiondat))
149                                         if err != nil {
150                                                 return nil, err
151                                         }
152
153                                         dirname := path.Join(pdir, dir.Name())
154                                         r, err := git.PlainOpen(dirname)
155                                         if err != nil {
156                                                 return nil, err
157                                         }
158
159                                         p := &Plugin{
160                                                 info:    info,
161                                                 dir:     dirname,
162                                                 repo:    r,
163                                                 version: sv,
164                                         }
165
166                                         plugins = append(plugins, p)
167                                 }
168                         }
169                 }
170         }
171         return plugins, nil
172 }