func NewResource(d map[string]interface{}, registry validator.Registry) (*Resource, error) {
	cpy, err := clone.Descriptor(d)
	if err != nil {
		return nil, err
	}
	if schStr, ok := cpy[schemaProp].(string); ok {
		cpy[schemaProp], err = loadSchema(schStr)
		if err != nil {
			return nil, err
		}
	}
	fillResourceDescriptorWithDefaultValues(cpy)
	profile, ok := cpy[profilePropName].(string)
	if !ok {
		return nil, fmt.Errorf("profile property MUST be a string:\"%s\"", profilePropName)
	}
	if err := validator.Validate(cpy, profile, registry); err != nil {
		return nil, err
	}
	r := Resource{
		descriptor: cpy,
		name:       cpy[nameProp].(string),
	}
	pathI := cpy[pathProp]
	if pathI != nil {
		p, err := parsePath(pathI, cpy)
		if err != nil {
			return nil, err
		}
		r.path = append([]string{}, p...)
		return &r, nil
	}
	dataI := cpy[dataProp]
	data, err := parseData(dataI, cpy)
	if err != nil {
		return nil, err
	}
	r.data = data
	return &r, nil
}
NewResource creates a new Resource from the passed-in descriptor, if valid. The
passed-in validator.Registry will be the source of profiles used in the validation.
package datapackage
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"
	"github.com/frictionlessdata/datapackage-go/clone"
	"github.com/frictionlessdata/datapackage-go/validator"
	"github.com/frictionlessdata/tableschema-go/csv"
	"github.com/frictionlessdata/tableschema-go/schema"
	"github.com/frictionlessdata/tableschema-go/table"
)
var tabularFormats = map[string]struct{}{
	"csv":  struct{}{},
	"tsv":  struct{}{},
	"xls":  struct{}{},
	"xlsx": struct{}{},
}
const (
	tabularDataResourceProfile = "tabular-data-resource"
)
type pathType byte
const (
	urlPath      pathType = 0
	relativePath pathType = 1
)
const (
	schemaProp           = "schema"
	nameProp             = "name"
	formatProp           = "format"
	mediaTypeProp        = "mediatype"
	pathProp             = "path"
	dataProp             = "data"
	jsonFormat           = "json"
	profileProp          = "profile"
	dialectProp          = "dialect"
	delimiterProp        = "delimiter"
	skipInitialSpaceProp = "skipInitialSpace"
	headerProp           = "header"
	doubleQuoteProp      = "doubleQuote"
)
type dialect struct {
	Delimiter rune
	SkipInitialSpace bool
	Header bool
	DoubleQuote bool
}
var defaultDialect = dialect{
	Delimiter:        ',',
	SkipInitialSpace: true,
	Header:           true,
	DoubleQuote:      true,
}
type Resource struct {
	descriptor map[string]interface{}
	path       []string
	data       interface{}
	name       string
	basePath   string
}
func (r *Resource) Name() string {
	return r.name
}
func (r *Resource) Descriptor() map[string]interface{} {
	c, _ := clone.Descriptor(r.descriptor)
	return c
}
func (r *Resource) Update(d map[string]interface{}, loaders ...validator.RegistryLoader) error {
	reg, err := validator.NewRegistry(loaders...)
	if err != nil {
		return err
	}
	res, err := NewResource(d, reg)
	if err != nil {
		return err
	}
	*r = *res
	return nil
}
func (r *Resource) Tabular() bool {
	if pStr, ok := r.descriptor[profileProp].(string); ok && pStr == tabularDataResourceProfile {
		return true
	}
	fStr, _ := r.descriptor[formatProp].(string)
	if _, ok := tabularFormats[fStr]; ok {
		return true
	}
	if len(r.path) > 0 && all(r.path, isFileTabular) {
		return true
	}
	return false
}
func all(strings []string, f func(string) bool) bool {
	for _, s := range strings {
		if !f(s) {
			return false
		}
	}
	return true
}
func isFileTabular(path string) bool {
	for extension := range tabularFormats {
		if strings.HasSuffix(path, extension) {
			return true
		}
	}
	return false
}
func dialectOpts(i interface{}) []csv.CreationOpts {
	if i == nil {
		return []csv.CreationOpts{}
	}
	d := defaultDialect
	dMap, ok := i.(map[string]interface{})
	if ok {
		if v, ok := dMap[delimiterProp].(string); ok {
			s := []rune(v)
			if len(s) > 0 {
				d.Delimiter = s[0]
			}
		}
		if v, ok := dMap[skipInitialSpaceProp].(bool); ok {
			d.SkipInitialSpace = v
		}
		if v, ok := dMap[headerProp].(bool); ok {
			d.Header = v
		}
	}
	opts := []csv.CreationOpts{csv.Delimiter(d.Delimiter)}
	if !d.SkipInitialSpace {
		opts = append(opts, csv.ConsiderInitialSpace())
	}
	if d.Header {
		opts = append(opts, csv.LoadHeaders())
	}
	return opts
}
func (r *Resource) GetTable(opts ...csv.CreationOpts) (table.Table, error) {
	if !r.Tabular() {
		return nil, fmt.Errorf("methods iter/read are not supported for non tabular data")
	}
	fullOpts := append(dialectOpts(r.descriptor[dialectProp]), opts...)
	if r.data != nil {
		switch r.data.(type) {
		case string:
			return csv.NewTable(csv.FromString(r.data.(string)), fullOpts...)
		default:
			return nil, fmt.Errorf("only csv and string is supported for inlining data")
		}
	}
	return csv.NewTable(func() (io.ReadCloser, error) { return loadContents(r.basePath, r.path, csvLoadFunc) }, fullOpts...)
}
func csvLoadFunc(p string) func() (io.ReadCloser, error) {
	if strings.HasPrefix(p, "http") {
		return csv.Remote(p)
	}
	return csv.FromFile(p)
}
const (
	remoteFetchTimeout = 15 * time.Second
)
var (
	httpClient      *http.Client
	startHTTPClient sync.Once
)
func binaryLoadFunc(p string) func() (io.ReadCloser, error) {
	if strings.HasPrefix(p, "http") {
		return func() (io.ReadCloser, error) {
			startHTTPClient.Do(func() {
				httpClient = &http.Client{
					Timeout: remoteFetchTimeout,
				}
			})
			resp, err := httpClient.Get(p)
			if err != nil {
				return nil, err
			}
			return resp.Body, nil
		}
	}
	return func() (io.ReadCloser, error) {
		return os.Open(p)
	}
}
type multiReadCloser struct {
	io.Reader
	rcs []io.ReadCloser
}
func (m *multiReadCloser) Close() error {
	var err error
	for _, rc := range m.rcs {
		if e := rc.Close(); e != nil {
			err = e
		}
	}
	return err
}
func newMultiReadCloser(rcs []io.ReadCloser) io.ReadCloser {
	readers := make([]io.Reader, len(rcs))
	for i := range rcs {
		readers[i] = io.Reader(rcs[i])
	}
	return &multiReadCloser{io.MultiReader(readers...), rcs}
}
func loadContents(basePath string, path []string, f func(string) func() (io.ReadCloser, error)) (io.ReadCloser, error) {
	var rcs []io.ReadCloser
	for _, p := range path {
		if basePath != "" {
			p = joinPaths(basePath, p)
		}
		rc, err := f(p)()
		if err != nil {
			return nil, err
		}
		rcs = append(rcs, rc)
		if len(path) > 1 {
			rcs = append(rcs, ioutil.NopCloser(bytes.NewReader([]byte{'\n'})))
		}
	}
	return newMultiReadCloser(rcs), nil
}
func joinPaths(basePath, path string) string {
	u, err := url.Parse(basePath)
	if err != nil {
		return filepath.Join(basePath, path)
	}
	u.Path = filepath.Join(u.EscapedPath(), path)
	return u.String()
}
func (r *Resource) ReadAll(opts ...csv.CreationOpts) ([][]string, error) {
	t, err := r.GetTable(opts...)
	if err != nil {
		return nil, err
	}
	return t.ReadAll()
}
func (r *Resource) RawRead() (io.ReadCloser, error) {
	if r.data != nil {
		return ioutil.NopCloser(bytes.NewReader([]byte(r.data.(string)))), nil
	}
	return loadContents(r.basePath, r.path, binaryLoadFunc)
}
func (r *Resource) Iter(opts ...csv.CreationOpts) (table.Iterator, error) {
	t, err := r.GetTable(opts...)
	if err != nil {
		return nil, err
	}
	return t.Iter()
}
func (r *Resource) GetSchema() (schema.Schema, error) {
	if r.descriptor[schemaProp] == nil {
		return schema.Schema{}, fmt.Errorf("schema is not declared in the descriptor")
	}
	buf, err := json.Marshal(r.descriptor[schemaProp])
	if err != nil {
		return schema.Schema{}, err
	}
	var s schema.Schema
	json.Unmarshal(buf, &s)
	return s, nil
}
func (r *Resource) Cast(out interface{}, opts ...csv.CreationOpts) error {
	sch, err := r.GetSchema()
	if err != nil {
		return err
	}
	tbl, err := r.GetTable(opts...)
	if err != nil {
		return err
	}
	return sch.CastTable(tbl, out)
}
func (r *Resource) CastColumn(name string, out interface{}, opts ...csv.CreationOpts) error {
	sch, err := r.GetSchema()
	if err != nil {
		return err
	}
	tab, err := r.GetTable(opts...)
	if err != nil {
		return err
	}
	col, err := tab.ReadColumn(name)
	if err != nil {
		return err
	}
	return sch.CastColumn(col, name, out)
}
func NewResourceWithDefaultRegistry(d map[string]interface{}) (*Resource, error) {
	reg, err := validator.NewRegistry()
	if err != nil {
		return nil, err
	}
	return NewResource(d, reg)
}