001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2024-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.common.yamljson;
019
020import dev.enola.common.io.resource.ReadableResource;
021import dev.enola.common.io.resource.WritableResource;
022
023import org.snakeyaml.engine.v2.api.Dump;
024import org.snakeyaml.engine.v2.api.DumpSettings;
025import org.snakeyaml.engine.v2.api.Load;
026import org.snakeyaml.engine.v2.api.LoadSettings;
027import org.snakeyaml.engine.v2.common.ScalarStyle;
028
029import java.io.IOException;
030import java.util.Map;
031import java.util.function.Consumer;
032
033/**
034 * YAML Utility.
035 *
036 * @deprecated Consider using the (newer) {@code
037 *     dev.enola.common.io.object.jackson.YamlObjectReaderWriter} and/or {@code
038 *     dev.enola.common.jackson.ObjectMappers} instead.
039 */
040@Deprecated
041public final class YAML {
042
043    private YAML() {}
044
045    private static Load newLoad() {
046        // NB: No need for new SafeConstructor(), here; because that's
047        // for org.yaml.snakeyaml, whereas this is for org.snakeyaml.engine.
048        var loadSettings = LoadSettings.builder();
049        // NB: Keep in-sync with similar (but not same, different API!) in
050        // dev.enola.common.jackson.ObjectMappers
051        loadSettings.setAllowDuplicateKeys(false);
052        loadSettings.setAllowRecursiveKeys(false);
053        loadSettings.setCodePointLimit(10 * 1024 * 1024); // 10 MB
054        return new Load(loadSettings.build());
055    }
056
057    private static Map<?, ?> iterable2singleMap(Iterable<?> iterable) {
058        var iterator = iterable.iterator();
059        if (!iterator.hasNext()) return Map.of();
060        var root = iterator.next();
061        if (root instanceof Map<?, ?> map) {
062            if (iterator.hasNext())
063                throw new IllegalArgumentException("YAML with multiple --- documents");
064            return map;
065        } else throw new IllegalArgumentException("YAML document is not Map");
066    }
067
068    @Deprecated
069    public static Iterable<?> read(String yaml) {
070        return newLoad().loadAllFromString(yaml);
071    }
072
073    @Deprecated
074    public static Map<?, ?> readSingleMap(String yaml) {
075        return iterable2singleMap(read(yaml));
076    }
077
078    @Deprecated
079    public static void read(ReadableResource yaml, Consumer<Iterable> itery) throws IOException {
080        try (var is = yaml.byteSource().openBufferedStream()) {
081            itery.accept(newLoad().loadAllFromInputStream(is));
082        }
083    }
084
085    @Deprecated
086    public static void readSingleMap(ReadableResource yaml, Consumer<Map<?, ?>> mappy)
087            throws IOException {
088        read(yaml, iterable -> mappy.accept(iterable2singleMap(iterable)));
089    }
090
091    @Deprecated
092    public static String write(Object object) {
093        DumpSettings settings =
094                DumpSettings.builder().setDefaultScalarStyle(ScalarStyle.PLAIN).build();
095        Dump dump = new Dump(settings);
096        return dump.dumpToString(object);
097    }
098
099    @Deprecated
100    public static void write(Object object, WritableResource yaml) throws IOException {
101        yaml.charSink().write(write(object));
102    }
103}