func (m DatasetMapFilter) Invert() (endpoint.FSMap, error) {
	if m.filterMode {
		return nil, errors.Errorf("can only invert mappings")
	}
	if len(m.entries) != 1 {
		return nil, errors.Errorf("inversion of complicated mappings is not implemented")  
	}
	e := m.entries[0]
	inv := &DatasetMapFilter{
		make([]datasetMapFilterEntry, len(m.entries)),
		false,
	}
	mp, err := zfs.NewDatasetPath(e.mapping)
	if err != nil {
		return nil, err
	}
	inv.entries[0] = datasetMapFilterEntry{
		path:         mp,
		mapping:      e.path.ToString(),
		subtreeMatch: e.subtreeMatch,
	}
	return inv, nil
}
FIXME investigate whether we can support more...
package filters
import (
	"fmt"
	"strings"
	"github.com/pkg/errors"
	"github.com/zrepl/zrepl/endpoint"
	"github.com/zrepl/zrepl/zfs"
)
type DatasetMapFilter struct {
	entries []datasetMapFilterEntry
	filterMode bool
}
type datasetMapFilterEntry struct {
	path *zfs.DatasetPath
	mapping      string
	subtreeMatch bool
}
func NewDatasetMapFilter(capacity int, filterMode bool) *DatasetMapFilter {
	return &DatasetMapFilter{
		entries:    make([]datasetMapFilterEntry, 0, capacity),
		filterMode: filterMode,
	}
}
func (m *DatasetMapFilter) Add(pathPattern, mapping string) (err error) {
	if m.filterMode {
		if _, err = m.parseDatasetFilterResult(mapping); err != nil {
			return
		}
	}
	const SUBTREE_PATTERN string = "<"
	patternCount := strings.Count(pathPattern, SUBTREE_PATTERN)
	switch {
	case patternCount > 1:
	case patternCount == 1 && !strings.HasSuffix(pathPattern, SUBTREE_PATTERN):
		err = fmt.Errorf("pattern invalid: only one '<' at end of string allowed")
		return
	}
	pathStr := strings.TrimSuffix(pathPattern, SUBTREE_PATTERN)
	path, err := zfs.NewDatasetPath(pathStr)
	if err != nil {
		return fmt.Errorf("pattern is not a dataset path: %s", err)
	}
	entry := datasetMapFilterEntry{
		path:         path,
		mapping:      mapping,
		subtreeMatch: patternCount > 0,
	}
	m.entries = append(m.entries, entry)
	return
}
func (m DatasetMapFilter) mostSpecificPrefixMapping(path *zfs.DatasetPath) (idx int, found bool) {
	lcp, lcp_entry_idx := -1, -1
	direct_idx := -1
	for e := range m.entries {
		entry := m.entries[e]
		ep := m.entries[e].path
		lep := ep.Length()
		switch {
		case !entry.subtreeMatch && ep.Equal(path):
			direct_idx = e
			continue
		case entry.subtreeMatch && path.HasPrefix(ep) && lep > lcp:
			lcp = lep
			lcp_entry_idx = e
		default:
			continue
		}
	}
	if lcp_entry_idx >= 0 || direct_idx >= 0 {
		found = true
		switch {
		case direct_idx >= 0:
			idx = direct_idx
		case lcp_entry_idx >= 0:
			idx = lcp_entry_idx
		}
	}
	return
}
func (m DatasetMapFilter) Map(source *zfs.DatasetPath) (target *zfs.DatasetPath, err error) {
	if m.filterMode {
		err = fmt.Errorf("using a filter for mapping simply does not work")
		return
	}
	mi, hasMapping := m.mostSpecificPrefixMapping(source)
	if !hasMapping {
		return nil, nil
	}
	me := m.entries[mi]
	if me.mapping == "" {
		if !me.subtreeMatch {
			return nil, fmt.Errorf("mapping to '' must be a subtree match")
		}
	} else {
		if strings.HasPrefix("!", me.mapping) {
			return nil, nil
		}
	}
	target, err = zfs.NewDatasetPath(me.mapping)
	if err != nil {
		err = fmt.Errorf("mapping target is not a dataset path: %s", err)
		return
	}
	if me.subtreeMatch {
		extendComps := source.Copy()
		extendComps.TrimPrefix(me.path)
		target.Extend(extendComps)
	}
	return
}
func (m DatasetMapFilter) Filter(p *zfs.DatasetPath) (pass bool, err error) {
	if !m.filterMode {
		err = fmt.Errorf("using a mapping as a filter does not work")
		return
	}
	mi, hasMapping := m.mostSpecificPrefixMapping(p)
	if !hasMapping {
		pass = false
		return
	}
	me := m.entries[mi]
	pass, err = m.parseDatasetFilterResult(me.mapping)
	return
}
func (m DatasetMapFilter) InvertedFilter() (inv *DatasetMapFilter, err error) {
	if m.filterMode {
		err = errors.Errorf("can only invert mappings")
		return
	}
	inv = &DatasetMapFilter{
		make([]datasetMapFilterEntry, len(m.entries)),
		true,
	}
	for i, e := range m.entries {
		inv.entries[i].path, err = zfs.NewDatasetPath(e.mapping)
		if err != nil {
			err = errors.Wrapf(err, "mapping cannot be inverted: '%s' is not a dataset path", e.mapping)
			return
		}
		inv.entries[i].mapping = MapFilterResultOk
		inv.entries[i].subtreeMatch = e.subtreeMatch
	}
	return inv, nil
}