diff --git a/pom.xml b/pom.xml index 0894a496f29..f6ef10667f8 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,7 @@ UTF-8 7.5.0 + 2.15.3 org.rascalmpl.shell.RascalShell 2 11 @@ -441,5 +442,15 @@ icu4j 69.1 + + com.fasterxml.jackson.dataformat + jackson-dataformat-toml + ${jackson-version} + + + com.fasterxml.jackson.module + jackson-module-parameter-names + ${jackson-version} + diff --git a/src/org/rascalmpl/interpreter/utils/RascalToml.java b/src/org/rascalmpl/interpreter/utils/RascalToml.java new file mode 100644 index 00000000000..6648eeb1db1 --- /dev/null +++ b/src/org/rascalmpl/interpreter/utils/RascalToml.java @@ -0,0 +1,154 @@ +package org.rascalmpl.interpreter.utils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Collections; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.zip.ZipEntry; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.rascalmpl.uri.URIResolverRegistry; +import org.rascalmpl.uri.URIUtil; + +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.dataformat.toml.TomlMapper; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; + +import io.usethesource.vallang.ISourceLocation; + +public class RascalToml { + private final String version; + + private final Project project; + private final @Nullable Main main; + + + private static class Project { + private final String name; + private final List sources; + private final @Nullable List libraries; + + private Project(String name, @Nullable List sources, @Nullable List libraries) { + this.name = name; + this.sources = (sources == null || sources.isEmpty()) ? Collections.singletonList("src") : Collections.unmodifiableList(sources); + this.libraries = (libraries == null || libraries.isEmpty()) ? null : Collections.unmodifiableList(libraries); + } + } + + private static class Main { + private final @Nullable String module; + private final @Nullable String function; + + private Main(@Nullable String module, @Nullable String function) { + this.module = module; + this.function = function; + } + } + + + private RascalToml(String version, Project project, @Nullable Main main) { + this.version = version; + this.project = project; + this.main = main; + } + + public String getVersion() { + return version; + } + + public @Nullable String getProjectName() { + return project.name; + } + + public @Nullable String getMainModule() { + return main.module; + } + + public @Nullable String getMainFunction() { + return main.function; + } + + public List getSource() { + return project.sources; + } + + public @Nullable List getRequireLibraries() { + return project.libraries; + } + + public static RascalToml parse(JarInputStream from) throws IOException { + return parse(tomlFile(from)); + } + + public static RascalToml parse(Class from) throws IOException { + return parse(tomlFile(from)); + } + + public static RascalToml parse(File from) throws IOException { + return parse(tomlFile(from)); + } + + public static RascalToml parse(ISourceLocation from) throws IOException { + return parse(tomlFile(from)); + } + + private static ObjectReader tomlMapper = new TomlMapper() + .registerModule(new ParameterNamesModule()) + .readerFor(RascalToml.class); + + public static RascalToml parse(Reader from) throws IOException { + return tomlMapper.readValue(from); + } + + public static RascalToml parse(InputStream from) throws IOException { + return tomlMapper.readValue(from); + } + + private static final String TOML_LOCATION = "META_INF/rascal.toml"; + + private static InputStream tomlFile(JarInputStream stream) throws IOException { + try { + JarEntry next = null; + while ((next = stream.getNextJarEntry()) != null) { + if (next.getName().equals(TOML_LOCATION)) { + return stream; // the stream now behaves as an input stream for this file entry + } + } + throw new IOException("Could not find: " + TOML_LOCATION); + } catch (IOException e) { + throw new IOException("Error iterating jar for " + TOML_LOCATION, e); + } + } + + private static InputStream tomlFile(File jarFile) throws IOException { + try (var file = new JarFile(jarFile)) { + return file.getInputStream(new ZipEntry(TOML_LOCATION)); + } + catch (IOException e) { + throw new IOException("Error loading " + TOML_LOCATION +" from " + jarFile, e); + } + } + + private static InputStream tomlFile(Class clazz) throws IOException { + var result = clazz.getResourceAsStream("/" + TOML_LOCATION); + if (result == null) { + throw new IOException("Error loading " + TOML_LOCATION + " from " + clazz); + } + return result; + } + + private static Reader tomlFile(ISourceLocation root) throws IOException { + try { + return URIResolverRegistry.getInstance() + .getCharacterReader(URIUtil.getChildLocation(RascalManifest.jarify(root), TOML_LOCATION)); + } catch (IOException e) { + throw new IOException("Error loading " + TOML_LOCATION +" from " + root, e); + } + } + +} diff --git a/test/org/rascalmpl/test/util/RascalTomlTests.java b/test/org/rascalmpl/test/util/RascalTomlTests.java new file mode 100644 index 00000000000..e632d239a16 --- /dev/null +++ b/test/org/rascalmpl/test/util/RascalTomlTests.java @@ -0,0 +1,38 @@ +package org.rascalmpl.test.util; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Collections; + +import org.junit.Test; +import org.rascalmpl.interpreter.utils.RascalToml; + +public class RascalTomlTests { + + private static RascalToml parse(String s) throws IOException { + System.out.println(s); + return RascalToml.parse(new StringReader(s)); + } + + @Test + public void parserWorks() throws IOException { + var toml = parse( + "version='1.0' \n" + + + "[project]\n" + + "name = 'test-project'\n" + + "sources = [ 'src/main/rascal']\n" + + "libraries = [ '|lib://typepal|']\n" + + + "[main]\n" + + "module = 'lang::testing::Mod'\n" + + "function = 'main'\n" + ); + assertEquals("1.0", toml.getVersion()); + assertEquals("test-project", toml.getProjectName()); + assertEquals(Collections.singletonList("src/main/rascal"), toml.getSource()); + assertEquals(Collections.singletonList("|lib://typepal|"), toml.getRequireLibraries()); + } +}