func ParseLine(n int, unsafeText []byte) (*Line, error) {
	linetype := LineTypePlain
	// trim off comments
	var comment []byte
	text := make([]byte, len(unsafeText))
	copy(text, unsafeText)
	if bytes.Contains(text, commentPrefix) {
		segs := bytes.Split(text, commentPrefix)
		text = segs[0]
		comment = segs[1]
	}
	var rx *regexp.Regexp
	for _, item := range matchTypes {
		if regexes[item.R].Match(text) {
			linetype = item.Type
			rx = regexes[item.R]
			break
		}
	}
	// parse the detail now
	var d *Detail
	if linetype == LineTypeDetail || linetype == LineTypeParam {
		var err error
		d, err = parseDetail(text, rx)
		if err != nil {
			return nil, &ErrLine{N: n, Err: err}
		}
	}
	return &Line{
		Number:  n,
		Type:    linetype,
		Bytes:   text,
		Regexp:  rx,
		Comment: comment,
		detail:  d,
	}, nil
}
func ParseLine(n int, unsafeText []byte) (*Line, error) {
	linetype := LineTypePlain
	// trim off comments
	var comment []byte
	text := make([]byte, len(unsafeText))
	copy(text, unsafeText)
	if bytes.Contains(text, commentPrefix) {
		segs := bytes.Split(text, commentPrefix)
		text = segs[0]
		comment = segs[1]
	}
	var rx *regexp.Regexp
	for _, item := range matchTypes {
		if regexes[item.R].Match(text) {
			linetype = item.Type
			rx = regexes[item.R]
			break
		}
	}
	// parse the detail now
	var d *Detail
	if linetype == LineTypeDetail || linetype == LineTypeParam {
		var err error
		d, err = parseDetail(text, rx)
		if err != nil {
			return nil, &ErrLine{N: n, Err: err}
		}
	}
	return &Line{
		Number:  n,
		Type:    linetype,
		Bytes:   text,
		Regexp:  rx,
		Comment: comment,
		detail:  d,
	}, nil
}
// ParseLine makes a new Line with the given data.