Explorar el Código

refactor(processor): parse ~ISO8601 in a proper way

Instead of using an ugly (and incomplete) regex, let's use a simple for-loop to
parse ISO8601 dates, and make it explicit that we're only supporting a subset
of the spec, as we only care about youtube video durations.
jvoisin hace 1 año
padre
commit
b48e6472f5
Se han modificado 1 ficheros con 32 adiciones y 29 borrados
  1. 32 29
      internal/reader/processor/utils.go

+ 32 - 29
internal/reader/processor/utils.go

@@ -6,53 +6,56 @@ package processor // import "miniflux.app/v2/internal/reader/processor"
 import (
 	"errors"
 	"fmt"
-	"regexp"
 	"strconv"
+	"strings"
 	"time"
 
 	"github.com/tdewolff/minify/v2"
 	"github.com/tdewolff/minify/v2/html"
 )
 
-// TODO: use something less horrible than a regex to parse ISO 8601 durations.
-
-var (
-	iso8601Regex = regexp.MustCompile(`^P((?P<year>\d+)Y)?((?P<month>\d+)M)?((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$`)
-)
-
+// parseISO8601 parses a restricted subset of ISO8601 dates, mainly for youtube video durations
 func parseISO8601(from string) (time.Duration, error) {
-	var match []string
-	var d time.Duration
-
-	if iso8601Regex.MatchString(from) {
-		match = iso8601Regex.FindStringSubmatch(from)
-	} else {
-		return 0, errors.New("processor: could not parse duration string")
+	after, ok := strings.CutPrefix(from, "PT")
+	if !ok {
+		return 0, errors.New("the period doesn't start with PT")
 	}
 
-	for i, name := range iso8601Regex.SubexpNames() {
-		part := match[i]
-		if i == 0 || name == "" || part == "" {
-			continue
-		}
+	var d time.Duration
+	num := ""
 
-		val, err := strconv.ParseInt(part, 10, 64)
-		if err != nil {
-			return 0, err
-		}
+	for _, char := range after {
+		var val float64
+		var err error
 
-		switch name {
-		case "hour":
+		switch char {
+		case 'Y', 'W', 'D':
+			return 0, fmt.Errorf("the '%c' specifier isn't supported", char)
+		case 'H':
+			if val, err = strconv.ParseFloat(num, 64); err != nil {
+				return 0, err
+			}
 			d += time.Duration(val) * time.Hour
-		case "minute":
+			num = ""
+		case 'M':
+			if val, err = strconv.ParseFloat(num, 64); err != nil {
+				return 0, err
+			}
 			d += time.Duration(val) * time.Minute
-		case "second":
+			num = ""
+		case 'S':
+			if val, err = strconv.ParseFloat(num, 64); err != nil {
+				return 0, err
+			}
 			d += time.Duration(val) * time.Second
+			num = ""
+		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
+			num += string(char)
+			continue
 		default:
-			return 0, fmt.Errorf("processor: unknown field %s", name)
+			return 0, errors.New("invalid character in the period")
 		}
 	}
-
 	return d, nil
 }