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  }