github.com/aleksi/gonuts.io@v0.0.0-20130622121132-3b0f2d1999fb/gopath/src/gonuts.io/AlekSi/nut/spec.go (about) 1 package nut 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/url" 9 "os" 10 "regexp" 11 "strings" 12 ) 13 14 // Describes part of nut meta-information, stored in file nut.json. 15 type Spec struct { 16 Version Version 17 Vendor string 18 Authors []Person 19 ExtraFiles []string 20 Homepage string 21 } 22 23 // Describes nut author. 24 type Person struct { 25 FullName string 26 Email string `json:",omitempty"` 27 } 28 29 const ( 30 ExampleFullName = "Crazy Nutter" 31 ExampleEmail = "crazy.nutter@gonuts.io" 32 SpecFileName = "nut.json" 33 ) 34 35 var VendorRegexp = regexp.MustCompile(`^[0-9a-z][0-9a-z_-]*$`) 36 37 // check interface 38 var ( 39 _ io.ReaderFrom = &Spec{} 40 _ io.WriterTo = &Spec{} 41 ) 42 43 // Reads spec from specified file. 44 func (spec *Spec) ReadFile(fileName string) (err error) { 45 f, err := os.Open(fileName) 46 if err != nil { 47 return 48 } 49 defer f.Close() 50 51 _, err = spec.ReadFrom(f) 52 return 53 } 54 55 // ReadFrom reads spec from r until EOF. 56 // The return value n is the number of bytes read. 57 // Any error except io.EOF encountered during the read is also returned. 58 // Implements io.ReaderFrom. 59 func (spec *Spec) ReadFrom(r io.Reader) (n int64, err error) { 60 var b []byte 61 b, err = ioutil.ReadAll(r) 62 n = int64(len(b)) 63 if err != nil { 64 return 65 } 66 67 err = json.Unmarshal(b, spec) 68 return 69 } 70 71 // WriteTo writes spec to w. 72 // The return value n is the number of bytes written. 73 // Any error encountered during the write is also returned. 74 // Implements io.WriterTo. 75 func (spec *Spec) WriteTo(w io.Writer) (n int64, err error) { 76 var b []byte 77 b, err = json.MarshalIndent(spec, "", " ") 78 if err != nil { 79 return 80 } 81 82 b = append(b, '\n') 83 n1, err := w.Write(b) 84 n = int64(n1) 85 return 86 } 87 88 // Checks spec for errors and return them. 89 func (spec *Spec) Check() (errors []string) { 90 // check version 91 if spec.Version.String() == "0.0.0" { 92 errors = append(errors, fmt.Sprintf("Version %q is invalid.", spec.Version)) 93 } 94 95 // check vendor 96 if !VendorRegexp.MatchString(spec.Vendor) { 97 errors = append(errors, fmt.Sprintf(`Vendor should contain only lower word characters (match "%s").`, VendorRegexp)) 98 } 99 100 // author should be specified 101 if len(spec.Authors) == 0 { 102 errors = append(errors, "No authors given.") 103 } else { 104 for _, a := range spec.Authors { 105 if a.FullName == ExampleFullName { 106 errors = append(errors, fmt.Sprintf("%q is not a real person.", a.FullName)) 107 } 108 } 109 } 110 111 // check license 112 licenseFound := false 113 for _, f := range spec.ExtraFiles { 114 f = strings.ToLower(f) 115 if strings.HasPrefix(f, "license") || strings.HasPrefix(f, "licence") || strings.HasPrefix(f, "copying") { 116 licenseFound = true 117 } 118 } 119 if !licenseFound { 120 errors = append(errors, "Spec should include license file in ExtraFiles.") 121 } 122 123 // check homepage 124 if spec.Homepage != "" { 125 u, err := url.Parse(spec.Homepage) 126 if err != nil { 127 errors = append(errors, fmt.Sprintf("Can't parse homepage: %s", err)) 128 } else { 129 if !u.IsAbs() || u.Opaque != "" || (u.Scheme != "http" && u.Scheme != "https") { 130 errors = append(errors, "Homepage should be absolute http:// or https:// URL.") 131 } 132 } 133 } 134 135 return 136 }