PercentKeyValuesFormatter.java

package io.jstach.rainbowgum.format;

import java.nio.charset.StandardCharsets;
import java.util.List;

import org.jspecify.annotations.Nullable;

import io.jstach.rainbowgum.KeyValues;
import io.jstach.rainbowgum.KeyValues.KeyValuesConsumer;
import io.jstach.rainbowgum.LogEvent;
import io.jstach.rainbowgum.LogFormatter;
import io.jstach.rainbowgum.LogFormatter.KeyValueNullStrategy;
import io.jstach.rainbowgum.PercentCodec;

/*
 * Percent encoded key values for KeyValuesFormatterBuilder: key=value pairs separated by &,
 * like a URI query. A null value (KEEP) is written as the key alone, without =, so it
 * stays distinct from an empty value.
 */
record PercentKeyValuesFormatter(@Nullable List<String> keys,
		KeyValueNullStrategy nullStrategy) implements LogFormatter.EventFormatter, KeyValuesConsumer<StringBuilder> {

	@Override
	public void format(StringBuilder output, LogEvent event) {
		var keyValues = event.keyValues();
		var selected = keys;
		if (selected == null) {
			keyValues.forEach(this, 0, output);
		}
		else {
			int count = 0;
			for (String key : selected) {
				count = accept(keyValues, key, keyValues.getValueOrNull(key), count, output);
			}
		}
	}

	@Override
	public int accept(KeyValues values, String key, @Nullable String value, int count, StringBuilder output) {
		if (value == null) {
			switch (nullStrategy) {
				case SKIP -> {
					return count;
				}
				case EMPTY -> value = "";
				case KEEP -> {
					/* written as the key alone */
				}
			}
		}
		if (count > 0) {
			output.append('&');
		}
		PercentCodec.encode(output, key, StandardCharsets.UTF_8);
		if (value != null) {
			output.append('=');
			PercentCodec.encode(output, value, StandardCharsets.UTF_8);
		}
		return count + 1;
	}

}