// A build stage creates a new build and adds all the files coming through the channel to // the Build and returns the result of Build as a File on the output channel. func Build(c *slurp.C, config Config) slurp.Stage { return func(in <-chan slurp.File, out chan<- slurp.File) { b := cache{config, make(map[string]*bytes.Buffer)} for file := range in { path, _ := filepath.Rel(file.Dir, file.Path) path = filepath.ToSlash(path) c.Infof("Adding %s", path) buff := new(bytes.Buffer) _, err := buff.ReadFrom(file) if err != nil { c.Error(err) } b.Files[path] = buff file.Close() //Close files AFTER we have build our package. } buff := new(bytes.Buffer) err := cacheTemplate.Execute(buff, b) if err != nil { c.Error(err) return } sf := slurp.File{ Reader: buff, Path: b.Name, } sf.FileInfo.SetName(b.Name) sf.FileInfo.SetSize(int64(buff.Len())) out <- sf } }
// Concatenates all the files from the input channel // and passes them to output channel with the given name. func Concat(c *slurp.C, name string) slurp.Stage { return func(files <-chan slurp.File, out chan<- slurp.File) { var ( size int64 bigfile = new(bytes.Buffer) ) for f := range files { c.Infof("Adding %s to %s", f.Path, name) n, err := bigfile.ReadFrom(f) if err != nil { c.Error(err) return } bigfile.WriteRune('\n') size += n + 1 f.Close() } fi := slurp.FileInfo{} fi.SetSize(size) fi.SetName(name) out <- slurp.File{ Reader: bigfile, Dir: "", Path: name, FileInfo: fi, } } }
//For The Glory of Debugging. func List(c *slurp.C) slurp.Stage { return func(files <-chan slurp.File, out chan<- slurp.File) { for f := range files { s, err := f.Stat() if err != nil { c.Error("Can't get File Stat name.") } else { c.Infof("slurp.File: %+v Name: %s", f, s.Name()) } out <- f } } }
//Filters out files based on a pattern, if they match, // they will be closed, otherwise sent to the output channel. func Filter(c *slurp.C, pattern string) slurp.Stage { return FilterFunc(c, func(f slurp.File) bool { s, err := f.Stat() if err != nil { c.Errorf("Can't get File Stat: %s", err.Error()) return false } m, err := glob.Match(pattern, s.Name()) if err != nil { c.Error(err) } return m }) }
func Compile(c *slurp.C) slurp.Stage { return func(in <-chan slurp.File, out chan<- slurp.File) { var wg sync.WaitGroup defer wg.Wait() for file := range in { // Skip underscored files like Ruby sass if string(filepath.Base(file.Path)[0]) == "_" { continue } wg.Add(1) go func(file slurp.File) { defer wg.Done() ctx := gosass.FileContext{ Options: gosass.Options{ OutputStyle: gosass.COMPRESSED_STYLE, IncludePaths: make([]string, 0), }, InputPath: file.Path, OutputString: "", ErrorStatus: 0, ErrorMessage: "", } gosass.CompileFile(&ctx) if ctx.ErrorStatus != 0 { c.Error("Sass error: ", ctx.ErrorMessage) return } buf := bytes.NewBufferString(ctx.OutputString) file.Reader = buf file.FileInfo.SetSize(int64(buf.Len())) out <- file }(file) } } }
// Copy the file content func Copy(c *slurp.C, keepath bool) slurp.Stage { return func(in <-chan slurp.File, out chan<- slurp.File) { fs := []*File{} var wg sync.WaitGroup defer wg.Wait() //Wait before all templates are executed. for file := range in { buf := new(bytes.Buffer) _, err := buf.ReadFrom(file.Reader) file.Close() if err != nil { c.Error(err) continue } s, err := file.Stat() if err != nil { c.Error(err) break } name := s.Name() file.Dir = "" if keepath { name = strings.Join(strings.Split(file.Path, "/")[1:], "/") file.Dir = strings.Split(file.Dir, "/")[0] } f := NewFile(name, file.Dir, buf.Bytes()) fs = append(fs, f) file.Path = name file.Reader = buf file.FileInfo.SetSize(int64(buf.Len())) out <- file } } }
// Unzip the zip files from input channel and pass the result // to the output channel. func Unzip(c *slurp.C) slurp.Stage { return func(in <-chan slurp.File, out chan<- slurp.File) { var wg sync.WaitGroup for file := range in { wg.Add(1) go func(file slurp.File) { defer wg.Done() raw, err := ioutil.ReadAll(file) file.Close() r, err := zip.NewReader(bytes.NewReader(raw), int64(len(raw))) if err != nil { c.Error(err) return } counter := c.Counter("unzipping", len(r.File)) // Iterate through the files in the archive, for i, f := range r.File { counter.Set(i+1, f.Name) content, err := f.Open() if err != nil { } fs := slurp.File{Reader: content, Dir: "", Path: f.Name, FileInfo: slurp.FileInfoFrom(f.FileInfo())} out <- fs } }(file) } wg.Wait() } }
// Dest writes the files from the input channel to the dst folder and closes the files. // It never returns Files. func Dest(c *slurp.C, dst string) slurp.Stage { return func(files <-chan slurp.File, out chan<- slurp.File) { var wg sync.WaitGroup defer wg.Wait() for file := range files { realpath, _ := filepath.Rel(file.Dir, file.Path) path := filepath.Join(dst, filepath.Dir(realpath)) err := os.MkdirAll(path, 0700) if err != nil { c.Error(err) return } if !file.FileInfo.IsDir() { wg.Add(1) go func(file slurp.File) { defer wg.Done() defer file.Close() realfile, err := os.Create(filepath.Join(dst, realpath)) if err != nil { c.Error(err) return } io.Copy(realfile, file) realfile.Close() }(file) } } } }
// bin is the binary name, it will be passed to os/exec.Command, so the same // path rules applies. // the args are the argumetns passed to the program. func Run(c *slurp.C, bin string, args ...string) slurp.Stage { return func(in <-chan slurp.File, out chan<- slurp.File) { var wg sync.WaitGroup defer wg.Wait() for file := range in { cmd := exec.Command(bin, args...) cmd.Stderr = os.Stderr //TODO: io.Writer logger. cmd.Stdin = file.Reader content, err := cmd.StdoutPipe() if err != nil { c.Error(err) return } err = cmd.Start() if err != nil { c.Error(err) return } wg.Add(1) go func(cmd *exec.Cmd) { defer wg.Done() defer slurp.Close(content) cmd.Wait() }(cmd) file.Reader = content out <- file } } }
//Src returns a channel of slurp.Files that match the provided pattern. func Src(c *slurp.C, globs ...string) slurp.Pipe { pipe := make(chan slurp.File) files, err := glob.Glob(globs...) if err != nil { c.Error(err) close(pipe) } cwd, err := os.Getwd() if err != nil { c.Error(err) close(pipe) return pipe } //TODO: Parse globs here, check for invalid globs, split them into "filters". go func() { defer close(pipe) for matchpair := range files { f, err := Read(matchpair.Name) if err != nil { c.Error(err) continue } f.Cwd = cwd f.Dir = glob.Dir(matchpair.Glob) pipe <- *f } }() return pipe }