github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/extractor/dir/dir.go (about)

     1  /*
     2   * Copyright (c) 2018-2020 vChain, Inc. All Rights Reserved.
     3   * This software is released under GPL3.
     4   * The full license information can be found under:
     5   * https://www.gnu.org/licenses/gpl-3.0.en.html
     6   *
     7   */
     8  
     9  package dir
    10  
    11  import (
    12  	"fmt"
    13  	"os"
    14  	"path/filepath"
    15  	"strings"
    16  
    17  	"github.com/vchain-us/vcn/pkg/api"
    18  	"github.com/vchain-us/vcn/pkg/bundle"
    19  	"github.com/vchain-us/vcn/pkg/extractor"
    20  	"github.com/vchain-us/vcn/pkg/uri"
    21  )
    22  
    23  // Scheme for dir
    24  const Scheme = "dir"
    25  
    26  // ManifestKey is the metadata's key for storing the manifest
    27  const ManifestKey = "manifest"
    28  
    29  // PathKey is the metadata's key for the directory path
    30  const PathKey = "path"
    31  
    32  type opts struct {
    33  	initIgnoreFile    bool
    34  	skipIgnoreFileErr bool
    35  }
    36  
    37  // Artifact returns a file *api.Artifact from a given u
    38  func Artifact(u *uri.URI, options ...extractor.Option) ([]*api.Artifact, error) {
    39  
    40  	if u.Scheme != Scheme {
    41  		return nil, nil
    42  	}
    43  
    44  	opts := &opts{}
    45  	if err := extractor.Options(options).Apply(opts); err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	path := strings.TrimPrefix(u.Opaque, "//")
    50  	path, err := filepath.Abs(path)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	d, err := os.Open(path)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	defer d.Close()
    60  
    61  	// get file info and check if is a directory
    62  	stat, err := d.Stat()
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	if !stat.IsDir() {
    67  		return nil, fmt.Errorf("read %s: is not a directory", path)
    68  	}
    69  
    70  	if opts.initIgnoreFile {
    71  		if err := InitIgnoreFile(path); err != nil {
    72  			if !opts.skipIgnoreFileErr {
    73  				return nil, err
    74  			}
    75  		}
    76  	}
    77  
    78  	files, err := Walk(path)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	manifest := bundle.NewManifest(files...)
    84  	digest, err := manifest.Digest()
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	// Metadata container
    90  	m := api.Metadata{
    91  		ManifestKey: manifest,
    92  		PathKey:     path,
    93  	}
    94  
    95  	return []*api.Artifact{{
    96  		Kind:     Scheme,
    97  		Hash:     digest.Encoded(),
    98  		Name:     stat.Name(),
    99  		Metadata: m,
   100  	}}, nil
   101  }
   102  
   103  // WithIgnoreFileInit returns a functional option to instruct the dir's extractor to create the defualt ignore file
   104  // when not yet present into the targeted directory.
   105  func WithIgnoreFileInit() extractor.Option {
   106  	return func(o interface{}) error {
   107  		if o, ok := o.(*opts); ok {
   108  			o.initIgnoreFile = true
   109  		}
   110  		return nil
   111  	}
   112  }
   113  
   114  // WithSkipIgnoreFileErr returns a functional option to instruct the dir's extractor to skip errors during
   115  // ignore file initialization.
   116  func WithSkipIgnoreFileErr() extractor.Option {
   117  	return func(o interface{}) error {
   118  		if o, ok := o.(*opts); ok {
   119  			o.skipIgnoreFileErr = true
   120  		}
   121  		return nil
   122  	}
   123  }