/* * Copyright (C) 2015 drrb * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.github.drrb.rust.netbeans.cargo; import com.github.drrb.rust.netbeans.parsing.RustLexUtils; import com.github.drrb.rust.netbeans.parsing.RustTokenId; import com.github.drrb.rust.netbeans.rustbridge.RustCrateType; import com.github.drrb.rust.netbeans.util.GsfUtilitiesHack; import com.google.common.collect.Iterables; import com.moandjiezana.toml.Toml; import java.io.File; import java.io.FileNotFoundException; import java.io.InputStreamReader; import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.file.Path; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.text.Document; import org.netbeans.api.lexer.TokenSequence; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; /** * */ public class CargoConfig { private static final Logger LOG = Logger.getLogger(CargoConfig.class.getName()); private final FileObject baseDir; public CargoConfig(FileObject baseDir) { this.baseDir = baseDir; } public Crate getOwningCrate(FileObject sourceFile) { for (Crate crate : getCrates()) { for (FileObject modFile : modFiles(crate.getFile())) { if (modFile.equals(sourceFile)) { return crate; } } } return new Crate(RustCrateType.RLIB, sourceFile); } private Iterable<? extends FileObject> modFiles(FileObject sourceFile) { final List<String> modDeclarations = new LinkedList<>(); final Document document = GsfUtilitiesHack.getDocument(sourceFile, true); //TODO: why do we need this hack? Why does NbDocument.get not work in tests document.render(new Runnable() { //TODO: apparently render() isn't read-locking the document (accoring to the test logs) @Override public void run() { TokenSequence<RustTokenId> rustTokens = new RustLexUtils().getRustTokenSequence(document, 0); lookingForModDeclarations: while (rustTokens.moveNext()) { if (rustTokens.token().id() == RustTokenId.MOD && rustTokens.moveNext()) { if (rustTokens.moveNext() && rustTokens.token().id() == RustTokenId.OPEN_BRACE) { // It's a mod literal continue lookingForModDeclarations; } else { modDeclarations.add(rustTokens.token().text().toString()); } } } } }); List<Iterable<? extends FileObject>> modFiles = new LinkedList<>(); modFiles.add(Collections.singleton(sourceFile)); for (String modName : modDeclarations) { FileObject parentDir = sourceFile.getParent(); FileObject possibleFileMod = parentDir.getFileObject(modName + ".rs"); FileObject possibleModDir = parentDir.getFileObject(modName); FileObject possibleDirMod = possibleModDir == null ? null : possibleModDir.getFileObject("mod.rs"); FileObject modFile = possibleFileMod == null ? possibleDirMod : possibleFileMod; if (modFile == null) { LOG.log(Level.WARNING, "Couldn''t find module ''{0}'' (found ''mod {0}'' in {1} but couldn''t find either ''{2}/{0}.rs'' or ''{2}/{0}/mod.rs'')", new Object[]{modName, sourceFile.getPath(), parentDir.getPath()}); continue; } modFiles.add(modFiles(modFile)); } return Iterables.concat(modFiles); } public List<Crate> getCrates() { //TODO: there could be errors in this config. Make sure we fail nicely Toml cargoConfig = getCargoConfig(); List<Crate> crates = new LinkedList<>(); Toml libCrate = cargoConfig.getTable("lib"); String libCratePath = libCrate.getString("path"); if (libCratePath != null) { FileObject crateFile = baseDir.getFileObject(libCratePath); List<String> libCrateTypes = new LinkedList<>(libCrate.getList("crate-type", String.class)); if (libCrateTypes.isEmpty()) { libCrateTypes.add("rlib"); } for (String libCrateType : libCrateTypes) { crates.add(new Crate(RustCrateType.forCargoName(libCrateType), crateFile)); } } List<Toml> binCrates = cargoConfig.getTables("bin"); for (Toml binCrate : binCrates) { String cratePath = binCrate.getString("path"); FileObject crateFile = baseDir.getFileObject(cratePath); crates.add(new Crate(RustCrateType.EXECUTABLE, crateFile)); } return crates; } public String getPackageName() { Toml cargoConf = getCargoConfig(); return cargoConf.getTable("package").getString("name"); } private Toml getCargoConfig() { FileObject cargoFile = baseDir.getFileObject("Cargo.toml"); if (cargoFile == null) { return new Toml(); } try { return new Toml().parse(new InputStreamReader(cargoFile.getInputStream(), UTF_8)); } catch (FileNotFoundException ex) { LOG.log(Level.WARNING, "Couldn't read crates from Cargo.toml", ex); return new Toml(); } } public String getModuleName(FileObject file) { if (isCrate(file)) { return ""; } FileObject sourceDir = baseDir.getFileObject("src"); String relativeModulePath = FileUtil.getRelativePath(sourceDir, file); return relativeModulePath .replaceAll("\\.rs$", "") .replaceAll("/mod$", "") // mod.rs files are named after their directory .replace("/", "::"); //TODO: should we use "/" here, or is it "\\" on Windows? } private boolean isCrate(FileObject file) { for (Crate crate : getCrates()) { if (crate.getFile().equals(file)) { return true; } } return false; } }