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

it.auties.protobuf.tool.command.UpdateCommand Maven / Gradle / Ivy

The newest version!
package it.auties.protobuf.tool.command;

import it.auties.protobuf.parser.ProtobufParser;
import it.auties.protobuf.parser.object.ProtobufDocument;
import it.auties.protobuf.parser.object.ProtobufObject;
import it.auties.protobuf.parser.statement.EnumConstantStatement;
import it.auties.protobuf.parser.statement.FieldStatement;
import it.auties.protobuf.parser.statement.ProtobufStatement;
import it.auties.protobuf.tool.util.LoggerUtils;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.Callable;

@Log4j2
@Command(
        name = "update",
        mixinStandardHelpOptions = true,
        version = "update 1.0",
        description = "Searches for updates to apply to model classes from a protobuf schema"
)
public class UpdateCommand implements Callable {
    @SuppressWarnings("FieldMayBeFinal")
    @Parameters(
            index = "0",
            description = "The protobuf file used to update the model classes"
    )
    private File protobuf = null;

    @SuppressWarnings("FieldMayBeFinal")
    @Parameters(
            index = "1",
            description = "The directory where the model classes are contained"
    )
    private File input = null;

    @SuppressWarnings("FieldMayBeFinal")
    @Option(
            names = {"-p", "--print"},
            description = "Print the names of missing files"
    )
    private boolean printNewFiles = false;

    @Override
    public Integer call() {
        if (!input.exists()) {
            log.error("The input directory %s doesn't exist".formatted(input));
            return -1;
        }

        try {
            LoggerUtils.suppressIllegalAccessWarning();
            var ast = generateAST();
            checkObject(ast);
            return 0;
        } catch (Throwable ex) {
            log.error("An uncaught exception was thrown, report this incident on github if you believe this to be a bug");
            log.throwing(ex);
            return -1;
        }
    }

    @SneakyThrows
    private void checkObject(ProtobufObject object) {
        if (object instanceof ProtobufDocument protobufDocument) {
            protobufDocument.getStatements().forEach(this::checkObject);
            return;
        }

        var file = findMatchingFile(object.getName());
        if (file.isEmpty()) {
            if (printNewFiles) {
                log.info("Detected new object: %s".formatted(object.getName()));
            }

            return;
        }

        var source = Files.readString(file.get());
        object.getStatements().forEach(fieldStatement -> checkStatement(object, fieldStatement, source));
    }

    private void checkStatement(ProtobufObject object, ProtobufStatement statement, String source) {
        if (statement instanceof ProtobufObject protobufObject) {
            checkObject(protobufObject);
            return;
        }

        if (statement instanceof EnumConstantStatement enumConstant) {
            if (source.contains("%s(%s)".formatted(enumConstant.getName(), enumConstant.getIndex()))) {
                return;
            }

            log.info("Detected new constant at %s: %s(%s)".formatted(object.getName(), enumConstant.getName(), enumConstant.getIndex()));
            return;
        }

        if (statement instanceof FieldStatement fieldStatement) {
            if (source.contains("%s %s".formatted(fieldStatement.getJavaType(), fieldStatement.getName()))) {
                return;
            }

            log.info("Detected new field at %s: %s(%s)".formatted(object.getName(), fieldStatement.getName(), fieldStatement.getIndex()));
            return;
        }

        throw new IllegalArgumentException("Cannot parse instruction: %s".formatted(statement.getClass().getName()));
    }

    private Optional findMatchingFile(String name) {
        try (var walker = Files.walk(input.toPath())) {
            return walker.filter(entry -> !Files.isDirectory(entry) && entry.getFileName().toString().equalsIgnoreCase(name + ".java"))
                    .findFirst();
        } catch (IOException exception) {
            throw new UncheckedIOException("Cannot search for matching model class", exception);
        }
    }

    private ProtobufDocument generateAST() throws IOException {
        log.info("Generating AST for protobuf file...");
        var parser = new ProtobufParser(protobuf);
        var document = parser.tokenizeAndParse();
        log.info("Generated AST successfully");
        return document;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy