class NumberSequence {

    public static Either<NumberSequence, String> parse(String value) {
        List<String> parts = StringExt.splitByComma(value);
        List<NumberSequence> subsequences = new ArrayList<>();
        for (String part : parts) {
            Matcher matcher = PART_RE.matcher(part);
            if (!matcher.matches()) {
                return Either.ofError(String.format("Syntax error in sequence fragment '%s'", part));
            }
            if (matcher.group(2) == null) {
                subsequences.add(new ValuesSequence(Integer.parseInt(part)));
            } else if (matcher.group(3) == null) {
                subsequences.add(new RangeSequence(Long.parseLong(matcher.group(1)), Long.MAX_VALUE, 1));
            } else {
                long from = Long.parseLong(matcher.group(1));
                long to = Long.parseLong(matcher.group(3));
                if (from >= to) {
                    return Either.ofError(String.format("Invalid range: %s (from) >= %s (to)", from, to));
                }
                subsequences.add(new RangeSequence(from, to, 1));
            }
        }
        return Either.ofValue(subsequences.isEmpty() ? EMPTY_SEQUENCE : new CompositeSequence(subsequences));
    }

}