001package org.consensusj.jsonrpc.cli; 002 003import com.fasterxml.jackson.core.JsonProcessingException; 004import com.fasterxml.jackson.databind.JsonNode; 005 006import java.util.stream.Stream; 007 008/** 009 * JSON-RPC method parameter, parsed from the command-line. There are two implementations: {@link Valid} and {@link Invalid}. 010 * A {@code Valid} contains an object that can be serialized to JSON via a converter/mapper like Jackson. An {@code Invalid} 011 * contains the {@link Exception} that occurred while parsing. 012 */ 013public sealed interface CliParameter { 014 /** 015 * Return the source (pre-deserialization) JSON string 016 * @return source string 017 */ 018 String source(); 019 020 default boolean valid() { 021 return this instanceof CliParameter.Valid; 022 } 023 024 default boolean invalid() { 025 return !valid(); 026 } 027 028 /** 029 * Stream zero or 1 valid objects. 030 * <p>This allows {@code .flatMap(CliParameter::stream)} to filter invalid and unwrap valid objects. 031 * @return A stream of zero or one valid objects. 032 */ 033 Stream<Object> stream(); 034 035 /** 036 * Create a {@code Valid} object 037 * @param source The source CLI string 038 * @param object The resulting object 039 * @return a valid parameter 040 */ 041 static Valid valid(String source, Object object) { 042 return new Valid(source, object); 043 } 044 045 /** 046 * Create a {@code Invalid} object 047 * @param source The source CLI string 048 * @param error The error returned from the parser 049 * @return a valid parameter 050 */ 051 static Invalid invalid(String source, Exception error) { 052 return new Invalid(source, error); 053 } 054 055 /** 056 * Parse a string returning either a valid or invalid {@code CliParameter} 057 * @param source the CLI parameter string to parse 058 * @param parseFunction a parsing function 059 * @return wraps either parsed, serializable object or an {@code Exception} 060 */ 061 static CliParameter parse(String source, Parser parseFunction) { 062 try { 063 return new CliParameter.Valid(source, parseFunction.apply(source)); 064 } catch (Exception e) { 065 return new CliParameter.Invalid(source, e); 066 } 067 } 068 069 /** 070 * Implementation for Valid objects 071 * @param source source JSON string 072 * @param object parsed object 073 */ 074 record Valid(String source, Object object) implements CliParameter { 075 public Stream<Object> stream() { return Stream.of(object); } 076 } 077 078 /** 079 * Implementation for Invalid source strings 080 * @param source source JSON string 081 * @param error parsing exception 082 */ 083 record Invalid(String source, Exception error) implements CliParameter { 084 public Stream<Object> stream() { return Stream.empty(); } 085 } 086 087 /** 088 * Functional interface for parsing a {@link String} to an {@link Object} possibly 089 * throwing an {@link Exception}. 090 */ 091 @FunctionalInterface 092 interface Parser { 093 JsonNode apply(String s) throws JsonProcessingException; 094 } 095}