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.convert;
019
020import com.google.common.primitives.UnsignedInteger;
021import com.google.common.primitives.UnsignedLong;
022
023import dev.enola.common.ByteSeq;
024
025import org.jspecify.annotations.Nullable;
026
027import java.io.IOException;
028import java.net.URISyntaxException;
029import java.nio.file.attribute.FileTime;
030import java.time.Instant;
031import java.time.LocalDate;
032import java.util.Optional;
033import java.util.UUID;
034
035public final class ObjectToStringBiConverters {
036
037    // TODO Add Locale aware Number converters (similar to TemporalAccessorToStringConverter)
038
039    public static final ObjectToStringBiConverter<String> STRING =
040            new ObjectToStringWithToStringBiConverter<>(String.class, input -> input);
041
042    public static final ObjectToStringBiConverter<Boolean> BOOLEAN =
043            new ObjectToStringWithToStringBiConverter<>(Boolean.class, Boolean::valueOf);
044
045    public static final ObjectToStringBiConverter<Byte> BYTE =
046            new ObjectToStringWithToStringBiConverter<>(Byte.class, Byte::valueOf);
047
048    public static final ObjectToStringBiConverter<Short> SHORT =
049            new ObjectToStringWithToStringBiConverter<>(Short.class, Short::valueOf);
050
051    // TODO UNSIGNED_BYTE
052
053    public static final ObjectToStringBiConverter<Short> UNSIGNED_SHORT =
054            new ObjectToStringBiConverter<>() {
055                @Override
056                public @Nullable String convertTo(@Nullable Short input)
057                        throws ConversionException {
058                    return Integer.toString(Short.toUnsignedInt(input));
059                }
060
061                @Override
062                public @Nullable Short convertFrom(@Nullable String input)
063                        throws ConversionException {
064                    throw new IllegalStateException("TODO: Not implemented yet, add tests!");
065                }
066            };
067
068    public static final ObjectToStringBiConverter<Integer> INT =
069            new ObjectToStringWithToStringBiConverter<>(Integer.class, Integer::valueOf);
070
071    public static final ObjectToStringBiConverter<UnsignedInteger> UNSIGNED_INTEGER =
072            new ObjectToStringWithToStringBiConverter<>(
073                    UnsignedInteger.class, UnsignedInteger::valueOf);
074
075    public static final ObjectToStringBiConverter<UnsignedLong> UNSIGNED_LONG =
076            new ObjectToStringWithToStringBiConverter<>(UnsignedLong.class, UnsignedLong::valueOf);
077
078    public static final ObjectToStringBiConverter<UUID> UUID =
079            new ObjectToStringWithToStringBiConverter<>(UUID.class, java.util.UUID::fromString);
080
081    public static final ObjectToStringBiConverter<java.net.URI> URI =
082            new ObjectToStringWithToStringBiConverter<>(
083                    java.net.URI.class,
084                    input -> {
085                        try {
086                            return new java.net.URI(input);
087                        } catch (URISyntaxException e) {
088                            throw new ConversionException(input, e);
089                        }
090                    });
091
092    public static final ObjectToStringBiConverter<LocalDate> LOCAL_DATE =
093            new ObjectToStringWithToStringBiConverter<>(LocalDate.class, LocalDate::parse);
094
095    public static final ObjectToStringBiConverter<Instant> INSTANT =
096            // new ObjectToStringWithToStringBiConverter<>(Instant.class, Instant::parse);
097            TemporalAccessorToStringConverter.INSTANT;
098
099    public static final ObjectToStringBiConverter<FileTime> FILE_TIME =
100            new ObjectToStringWithToStringBiConverter<>(
101                    // TODO According to FileTime#Instant(), it "can store points on the time-line
102                    // further in the future and further in the past than Instant" - so no good?!
103                    FileTime.class, input -> FileTime.from(Instant.parse(input))) {
104                @Override
105                @SuppressWarnings("unchecked")
106                public <X> Optional<X> convertToType(FileTime input, Class<X> type)
107                        throws IOException {
108                    if (Instant.class.equals(type))
109                        return (Optional<X>) Optional.of(input.toInstant());
110                    return super.convertToType(input, type);
111                }
112            };
113
114    public static final ObjectToStringBiConverter<ByteSeq> MULTIBASE = new MultibaseConverter();
115
116    private ObjectToStringBiConverters() {}
117}