func (r *Resource) Iter(opts ...csv.CreationOpts) (table.Iterator, error) {
	t, err := r.GetTable(opts...)
	if err != nil {
		return nil, err
	}
	return t.Iter()
}
Iter returns an Iterator to read the tabular resource. Iter returns an error
if the table physical source can not be iterated.
The iteration process always start at the beginning of the table.
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)
}