func (m *M) Minify(mediatype string, w io.Writer, r io.Reader) error {
	mimetype, params := parse.Mediatype([]byte(mediatype))
	return m.MinifyMimetype(mimetype, w, r, params)
}
Minify minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
An error is returned when no such mimetype exists (ErrNotExist) or when an error occurred in the minifier function.
Mediatype may take the form of 'text/plain', 'text/*', '*/*' or 'text/plain; charset=UTF-8; version=2.0'.
package minify
import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
	"sync"
	"github.com/tdewolff/parse/v2"
	"github.com/tdewolff/parse/v2/buffer"
)
var Warning = log.New(os.Stderr, "WARNING: ", 0)
var ErrNotExist = errors.New("minifier does not exist for mimetype")
type MinifierFunc func(*M, io.Writer, io.Reader, map[string]string) error
func (f MinifierFunc) Minify(m *M, w io.Writer, r io.Reader, params map[string]string) error {
	return f(m, w, r, params)
}
type Minifier interface {
	Minify(*M, io.Writer, io.Reader, map[string]string) error
}
type patternMinifier struct {
	pattern *regexp.Regexp
	Minifier
}
type cmdMinifier struct {
	cmd *exec.Cmd
}
var cmdArgExtension = regexp.MustCompile(`^\.[0-9a-zA-Z]+`)
func (c *cmdMinifier) Minify(_ *M, w io.Writer, r io.Reader, _ map[string]string) error {
	cmd := &exec.Cmd{}
	*cmd = *c.cmd  
	var in, out *os.File
	for i, arg := range cmd.Args {
		if j := strings.Index(arg, "$in"); j != -1 {
			var err error
			ext := cmdArgExtension.FindString(arg[j+3:])
			if in, err = ioutil.TempFile("", "minify-in-*"+ext); err != nil {
				return err
			}
			cmd.Args[i] = arg[:j] + in.Name() + arg[j+3+len(ext):]
		} else if j := strings.Index(arg, "$out"); j != -1 {
			var err error
			ext := cmdArgExtension.FindString(arg[j+4:])
			if out, err = ioutil.TempFile("", "minify-out-*"+ext); err != nil {
				return err
			}
			cmd.Args[i] = arg[:j] + out.Name() + arg[j+4+len(ext):]
		}
	}
	if in == nil {
		cmd.Stdin = r
	} else if _, err := io.Copy(in, r); err != nil {
		return err
	}
	if out == nil {
		cmd.Stdout = w
	} else {
		defer io.Copy(w, out)
	}
	stderr := &bytes.Buffer{}
	cmd.Stderr = stderr
	err := cmd.Run()
	if _, ok := err.(*exec.ExitError); ok {
		if stderr.Len() != 0 {
			err = fmt.Errorf("%s", stderr.String())
		}
		err = fmt.Errorf("command %s failed: %w", cmd.Path, err)
	}
	return err
}
type M struct {
	mutex   sync.RWMutex
	literal map[string]Minifier
	pattern []patternMinifier
	URL *url.URL
}
func New() *M {
	return &M{
		sync.RWMutex{},
		map[string]Minifier{},
		[]patternMinifier{},
		nil,
	}
}
func (m *M) Add(mimetype string, minifier Minifier) {
	m.mutex.Lock()
	m.literal[mimetype] = minifier
	m.mutex.Unlock()
}
func (m *M) AddFunc(mimetype string, minifier MinifierFunc) {
	m.mutex.Lock()
	m.literal[mimetype] = minifier
	m.mutex.Unlock()
}
func (m *M) AddRegexp(pattern *regexp.Regexp, minifier Minifier) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
	m.mutex.Unlock()
}
func (m *M) AddFuncRegexp(pattern *regexp.Regexp, minifier MinifierFunc) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
	m.mutex.Unlock()
}
func (m *M) AddCmd(mimetype string, cmd *exec.Cmd) {
	m.mutex.Lock()
	m.literal[mimetype] = &cmdMinifier{cmd}
	m.mutex.Unlock()
}
func (m *M) AddCmdRegexp(pattern *regexp.Regexp, cmd *exec.Cmd) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, &cmdMinifier{cmd}})
	m.mutex.Unlock()
}
func (m *M) Match(mediatype string) (string, map[string]string, MinifierFunc) {
	m.mutex.RLock()
	defer m.mutex.RUnlock()
	mimetype, params := parse.Mediatype([]byte(mediatype))
	if minifier, ok := m.literal[string(mimetype)]; ok {  
		return string(mimetype), params, minifier.Minify
	}
	for _, minifier := range m.pattern {
		if minifier.pattern.Match(mimetype) {
			return minifier.pattern.String(), params, minifier.Minify
		}
	}
	return string(mimetype), params, nil
}