All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datakernel.http.decoder.Decoder Maven / Gradle / Ivy

package io.datakernel.http.decoder;

import io.datakernel.functional.Either;
import io.datakernel.http.HttpRequest;
import io.datakernel.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * A high-level API that allows declarative definition of HTTP parsers
 * that can convert incoming requests to concrete objects.
 * This allows complex parsers to be algebraically built from simple ones.
 */
public interface Decoder {
	/**
	 * Either return the parsed type or format
	 */
	Either decode(@NotNull HttpRequest request);

	@Nullable
	default T decodeOrNull(@NotNull HttpRequest request) {
		return decode(request).getLeftOrNull();
	}

	default T decodeOrThrow(@NotNull HttpRequest request) throws DecodeException {
		Either either = decode(request);
		if (either.isLeft()) return either.getLeft();
		throw new DecodeException(either.getRight());
	}

	/**
	 * An id that is going to be used in the error-tree if at some point the whole parser fails
	 */
	String getId();

	default Decoder withId(String id) {
		return new Decoder() {
			@Override
			public Either decode(@NotNull HttpRequest request) {
				return Decoder.this.decode(request);
			}

			@Override
			public String getId() {
				return id;
			}
		};
	}

	default  Decoder map(Function fn) {
		return mapEx(Mapper.of(fn));
	}

	default  Decoder map(Function fn, String message) {
		return mapEx(Mapper.of(fn, message));
	}

	/**
	 * Enhanced functional 'map' operation.
	 * If mapped returns an errors, then the returned decoder fails with that error.
	 */
	default  Decoder mapEx(Mapper fn) {
		return new AbstractDecoder(getId()) {
			@Override
			public Either decode(@NotNull HttpRequest request) {
				return Decoder.this.decode(request)
						.flatMapLeft(value ->
								fn.map(value)
										.mapRight(DecodeErrors::of));
			}
		};
	}

	default Decoder validate(Predicate predicate, String error) {
		return validate(Validator.of(predicate, error));
	}

	/**
	 * Enhanced functional 'filter' operation.
	 * If validator returns non-empty list of errors,
	 * then the returned decoder fails with these errors.
	 */
	default Decoder validate(Validator validator) {
		return new AbstractDecoder(getId()) {
			@Override
			public Either decode(@NotNull HttpRequest request) {
				Either decodedValue = Decoder.this.decode(request);
				if (decodedValue.isRight()) return decodedValue;
				List errors = validator.validate(decodedValue.getLeft());
				if (errors.isEmpty()) return decodedValue;
				return Either.right(DecodeErrors.of(errors));
			}
		};
	}

	@NotNull
	static  Decoder create(Function constructor, String message, Decoder... decoders) {
		return createEx(Mapper.of(constructor, message), decoders);
	}

	@NotNull
	static  Decoder create(Function constructor, Decoder... decoders) {
		return createEx(Mapper.of(constructor), decoders);
	}

	/**
	 * Plainly combines given decoders (they are called on the same request) into one, mapping the result
	 * with the supplied mapper.
	 */
	@NotNull
	static  Decoder createEx(Mapper constructor, Decoder... decoders) {
		return new AbstractDecoder("") {
			@Override
			public Either decode(@NotNull HttpRequest request) {
				Object[] args = new Object[decoders.length];
				DecodeErrors errors = DecodeErrors.create();
				for (int i = 0; i < decoders.length; i++) {
					Decoder decoder = decoders[i];
					Either decoded = decoder.decode(request);
					if (decoded.isLeft()) {
						args[i] = decoded.getLeft();
					} else {
						errors.with(decoder.getId(), decoded.getRight());
					}
				}
				if (errors.hasErrors()) {
					return Either.right(errors);
				}
				return constructor.map(args)
						.mapRight(DecodeErrors::of);
			}
		};
	}

	@SuppressWarnings("unchecked")
	@NotNull
	static  Decoder of(TupleConstructor1 constructor, Decoder param1) {
		return create(params -> constructor.create((T1) params[0]),
				param1);
	}

	@SuppressWarnings("unchecked")
	@NotNull
	static  Decoder of(TupleConstructor2 constructor,
			Decoder param1,
			Decoder param2) {
		return create(params -> constructor.create((T1) params[0], (T2) params[2]),
				param1, param2);
	}

	@SuppressWarnings("unchecked")
	@NotNull
	static  Decoder of(TupleConstructor3 constructor,
			Decoder param1,
			Decoder param2,
			Decoder param3) {
		return create(params -> constructor.create((T1) params[0], (T2) params[1], (T3) params[2]),
				param1, param2, param3);
	}

	@SuppressWarnings("unchecked")
	@NotNull
	static  Decoder of(TupleConstructor4 constructor,
			Decoder param1,
			Decoder param2,
			Decoder param3,
			Decoder param4) {
		return create(params -> constructor.create((T1) params[0], (T2) params[1], (T3) params[2], (T4) params[3]),
				param1, param2, param3, param4);
	}

	@SuppressWarnings("unchecked")
	@NotNull
	static  Decoder of(TupleConstructor5 constructor,
			Decoder param1,
			Decoder param2,
			Decoder param3,
			Decoder param4,
			Decoder param5) {
		return create(params -> constructor.create((T1) params[0], (T2) params[1], (T3) params[2], (T4) params[3], (T5) params[4]),
				param1, param2, param3, param4, param5);
	}

	@SuppressWarnings("unchecked")
	@NotNull
	static  Decoder of(TupleConstructor6 constructor,
			Decoder param1,
			Decoder param2,
			Decoder param3,
			Decoder param4,
			Decoder param5,
			Decoder param6) {
		return create(params -> constructor.create((T1) params[0], (T2) params[1], (T3) params[2], (T4) params[3], (T5) params[5], (T6) params[6]),
				param1, param2, param3, param4, param5, param6);
	}
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy