// Path: src/main/java/com/github/tonivade/resp/protocol/RedisToken.java
// static RedisToken array(RedisToken... redisTokens) {
//   return new ArrayRedisToken(ImmutableList.of(redisTokens));
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/RedisToken.java
// static RedisToken error(String str) {
//   return new ErrorRedisToken(str);
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/RedisToken.java
// static RedisToken status(String str) {
//   return new StatusRedisToken(str);
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/AbstractRedisToken.java
// public static final class IntegerRedisToken extends AbstractRedisToken<Integer> {
// 
//   IntegerRedisToken(Integer value) {
//     super(RedisTokenType.INTEGER, value);
//   }
// 
//   @Override
//   public <T> T accept(RedisTokenVisitor<T> visitor) {
//     return visitor.integer(this);
//   }
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/AbstractRedisToken.java
// public static final class StringRedisToken extends AbstractRedisToken<SafeString> {
// 
//   StringRedisToken(SafeString value) {
//     super(RedisTokenType.STRING, value);
//   }
// 
//   @Override
//   public <T> T accept(RedisTokenVisitor<T> visitor) {
//     return visitor.string(this);
//   }
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/AbstractRedisToken.java
// public static final class UnknownRedisToken extends AbstractRedisToken<SafeString> {
// 
//   UnknownRedisToken(SafeString value) {
//     super(RedisTokenType.UNKNOWN, value);
//   }
// 
//   @Override
//   public <T> T accept(RedisTokenVisitor<T> visitor) {
//     return visitor.unknown(this);
//   }
// }


import static com.github.tonivade.purefun.Precondition.checkNonNull;
import static com.github.tonivade.resp.protocol.RedisToken.array;
import static com.github.tonivade.resp.protocol.RedisToken.error;
import static com.github.tonivade.resp.protocol.RedisToken.status;
import static java.util.Spliterators.spliteratorUnknownSize;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import com.github.tonivade.resp.protocol.AbstractRedisToken.IntegerRedisToken;
import com.github.tonivade.resp.protocol.AbstractRedisToken.StringRedisToken;
import com.github.tonivade.resp.protocol.AbstractRedisToken.UnknownRedisToken;

  @Override
  public boolean hasNext() {
    return source.available() > 0;
  }

  @Override
  public RedisToken next() {
    return parseToken(source.readLine());
  }

  public <T> Stream<T> stream(Iterator<T> iterator) {
    return StreamSupport.stream(spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
  }

  private RedisToken parseToken(SafeString line) {
    RedisToken token = new UnknownRedisToken(SafeString.EMPTY_STRING);
    if (line != null && !line.isEmpty()) {
      if (line.startsWith(ARRAY_PREFIX)) {
        int size = Integer.parseInt(line.substring(1));
        token = parseArray(size);
      } else if (line.startsWith(STATUS_PREFIX)) {
        token = status(line.substring(1));
      } else if (line.startsWith(ERROR_PREFIX)) {
        token = error(line.substring(1));
      } else if (line.startsWith(INTEGER_PREFIX)) {
        token = parseIntegerToken(line);
      } else if (line.startsWith(STRING_PREFIX)) {
        token = parseStringToken(line);
      } else {
// Path: src/main/java/com/github/tonivade/resp/protocol/RedisToken.java
// static RedisToken array(RedisToken... redisTokens) {
//   return new ArrayRedisToken(ImmutableList.of(redisTokens));
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/RedisToken.java
// static RedisToken error(String str) {
//   return new ErrorRedisToken(str);
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/RedisToken.java
// static RedisToken status(String str) {
//   return new StatusRedisToken(str);
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/AbstractRedisToken.java
// public static final class IntegerRedisToken extends AbstractRedisToken<Integer> {
// 
//   IntegerRedisToken(Integer value) {
//     super(RedisTokenType.INTEGER, value);
//   }
// 
//   @Override
//   public <T> T accept(RedisTokenVisitor<T> visitor) {
//     return visitor.integer(this);
//   }
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/AbstractRedisToken.java
// public static final class StringRedisToken extends AbstractRedisToken<SafeString> {
// 
//   StringRedisToken(SafeString value) {
//     super(RedisTokenType.STRING, value);
//   }
// 
//   @Override
//   public <T> T accept(RedisTokenVisitor<T> visitor) {
//     return visitor.string(this);
//   }
// }
// 
// Path: src/main/java/com/github/tonivade/resp/protocol/AbstractRedisToken.java
// public static final class UnknownRedisToken extends AbstractRedisToken<SafeString> {
// 
//   UnknownRedisToken(SafeString value) {
//     super(RedisTokenType.UNKNOWN, value);
//   }
// 
//   @Override
//   public <T> T accept(RedisTokenVisitor<T> visitor) {
//     return visitor.unknown(this);
//   }
// }


// Path: src/main/java/com/github/tonivade/resp/protocol/RedisParser.java
import static com.github.tonivade.purefun.Precondition.checkNonNull;
import static com.github.tonivade.resp.protocol.RedisToken.array;
import static com.github.tonivade.resp.protocol.RedisToken.error;
import static com.github.tonivade.resp.protocol.RedisToken.status;
import static java.util.Spliterators.spliteratorUnknownSize;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import com.github.tonivade.resp.protocol.AbstractRedisToken.IntegerRedisToken;
import com.github.tonivade.resp.protocol.AbstractRedisToken.StringRedisToken;
import com.github.tonivade.resp.protocol.AbstractRedisToken.UnknownRedisToken;


  @Override
  public boolean hasNext() {
    return source.available() > 0;
  }

  @Override
  public RedisToken next() {
    return parseToken(source.readLine());
  }

  public <T> Stream<T> stream(Iterator<T> iterator) {
    return StreamSupport.stream(spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
  }

  private RedisToken parseToken(SafeString line) {
    RedisToken token = new UnknownRedisToken(SafeString.EMPTY_STRING);
    if (line != null && !line.isEmpty()) {
      if (line.startsWith(ARRAY_PREFIX)) {
        int size = Integer.parseInt(line.substring(1));
        token = parseArray(size);
      } else if (line.startsWith(STATUS_PREFIX)) {
        token = status(line.substring(1));
      } else if (line.startsWith(ERROR_PREFIX)) {
        token = error(line.substring(1));
      } else if (line.startsWith(INTEGER_PREFIX)) {
        token = parseIntegerToken(line);
      } else if (line.startsWith(STRING_PREFIX)) {
        token = parseStringToken(line);
      } else {
        token = new UnknownRedisToken(line);