Skip to content

Commit

Permalink
Add injection capability to the xec command line (#201)
Browse files Browse the repository at this point in the history
This is the first step to implement #199
  • Loading branch information
cpurdy authored Mar 28, 2024
1 parent c91c869 commit b85aacf
Show file tree
Hide file tree
Showing 3 changed files with 366 additions and 9 deletions.
71 changes: 68 additions & 3 deletions javatools/src/main/java/org/xvm/tool/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import static org.xvm.tool.ModuleInfo.isExplicitCompiledFile;

import static org.xvm.util.Handy.parseDelimitedString;
import static org.xvm.util.Handy.parseStringMap;
import static org.xvm.util.Handy.quotedString;
import static org.xvm.util.Handy.resolveFile;
import static org.xvm.util.Handy.toPathString;
Expand Down Expand Up @@ -725,6 +726,32 @@ public boolean specify(String sName, List<File> listFiles)
}
}

/**
* Register a value for the specified option of the Pair Form (key/value).
*
* @param sName the name of the option
* @param pairs the key/value pairs
*
* @return true if the value is accepted; false if the value represents an error
*/
public boolean specify(String sName, Map<String,String> pairs)
{
boolean fMulti;
if (formOf(sName) == Form.Pair &&
((fMulti = allowMultiple(sName)) || !values().containsKey(sName) && pairs.size() <= 1))
{
if (!pairs.isEmpty())
{
store(sName, fMulti, pairs);
}
return true;
}
else
{
return false;
}
}

/**
* @return true if a "show version" option has been specified
*/
Expand Down Expand Up @@ -770,10 +797,12 @@ public boolean parse(String[] asArgs)
// -arg // no value ("specified" for Name form)
// -arg=value // '=' delimiter between arg and value
// -arg value // value is in the next arg
// -arg key=value // pair is the next arg (for Form=Pair)
// 2) for any single "linux" argument:
// --arg // no value ("specified" for Name form)
// --arg=value // '=' delimiter between arg and value
// --arg value // value is in the next arg
// --arg key=value // pair is the next arg (for Form=Pair)
// 3) for multiple "posix" arguments (imagine that -A -B -C are all legal)
// -ABC // no value ("specified" for Name form)
int cch = sArg.length();
Expand Down Expand Up @@ -1037,6 +1066,13 @@ else if (file.exists())
oVal = repo;
}
break;

case Pair:
if (!sArg.isEmpty())
{
oVal = parseStringMap(sArg);
}
break;
}

if (oVal == null)
Expand Down Expand Up @@ -1122,7 +1158,21 @@ public String toString()
.append(sName);
}

if (fMulti || oVal instanceof List)
if (oVal instanceof Map)
{
sb.append(":\n");
Map<String, String> map = (Map) oVal;
for (Map.Entry<String, String> e : map.entrySet())
{
sb.append(sIndent)
.append(" ")
.append(entry.getKey())
.append("=")
.append(quotedString(String.valueOf(entry.getValue())))
.append('\n');
}
}
else if (fMulti || oVal instanceof List)
{
sb.append(":\n");
List list = (List) oVal;
Expand Down Expand Up @@ -1158,7 +1208,6 @@ else if (form == Form.Name)
* @param sName the option name
* @param fMulti true if the option can have multiple value
* @param value the value to store, or append to the ArrayList for that option name
*
*/
protected void store(String sName, boolean fMulti, Object value)
{
Expand All @@ -1171,6 +1220,15 @@ protected void store(String sName, boolean fMulti, Object value)
return list;
});
}
else if (value instanceof Map)
{
values().compute(sName, (k, v) ->
{
Map map = v == null ? new ListMap() : (ListMap) v;
map.putAll((Map) value);
return map;
});
}
else if (fMulti)
{
values().compute(sName, (k, v) ->
Expand Down Expand Up @@ -1777,8 +1835,14 @@ public String toString()
* e.g. "{@code --name="Bob"}" or "{@code --name "Bob"}"
* </li><li><tt>File</tt> - a File valued option;
* e.g. "{@code --src=./My.x}" or "{@code --src ./My.x}"
* </li><li><tt>FileList</tt> - a colon-delimited search path valued option;
* </li><li><tt>Repo</tt> - a colon-delimited search path valued option;
* e.g. "{@code -L ~/lib:./lib:./}" or "{@code -L~/lib:./}"
* </li><li><tt>Pair</tt> - an equal-sign-delimited key/value pairing, comma-delimited for
* multi; e.g. "{@code -I x=0,y=1,s="'Hello, = World!'"} (and note
* that the use of spaces requires the value to be
* quoted, and the occurrence of ',' or '=' within a value requires
* the value to be quoted again using single quotes, because the
* Java command line removes the double quotes)
* </li><li><tt>AsIs</tt> - an AsIs valued option is a String that is not modified, useful
* when being passed on to a further "argv-aware" program
* e.g. "{@code xec MyApp.xtc -o=7 -X="q"} -> {@code -o=7 -X="q"}"
Expand All @@ -1792,6 +1856,7 @@ public enum Form
String,
File,
Repo('\"' + java.io.File.pathSeparator + "\"-delimited File list"),
Pair("\"key=value\" Pair"),
AsIs;

Form()
Expand Down
22 changes: 16 additions & 6 deletions javatools/src/main/java/org/xvm/tool/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.xvm.api.Connector;
Expand Down Expand Up @@ -476,12 +477,13 @@ public Options()
{
super();

addOption("L" , null, Form.Repo, true, "Module path; a \"" + File.pathSeparator + "\"-delimited list of file and/or directory names");
addOption("M", "method", Form.String, false, "Method name; defaults to \"run\"");
addOption(null, "no-recompile", Form.Name, false, "Disable automatic compilation");
addOption("o", null, Form.File, false, "If compilation is necessary, the file or directory to write compiler output to");
addOption(Trailing, null, Form.File, false, "Module file name (.xtc) to execute");
addOption(ArgV, null, Form.AsIs, true, "Arguments to pass to the method");
addOption("I" , "inject", Form.Pair, true, "Specifies name/value pairs for injection; the format is \"name1=value1,name2=value2\"");
addOption("L" , null, Form.Repo, true, "Module path; a \"" + File.pathSeparator + "\"-delimited list of file and/or directory names");
addOption("M", "method", Form.String, false, "Method name; defaults to \"run\"");
addOption(null, "no-recompile", Form.Name, false, "Disable automatic compilation");
addOption("o", null, Form.File, false, "If compilation is necessary, the file or directory to write compiler output to");
addOption(Trailing, null, Form.File, false, "Module file name (.xtc) to execute");
addOption(ArgV, null, Form.AsIs, true, "Arguments to pass to the method");
}

/**
Expand Down Expand Up @@ -527,6 +529,14 @@ public String[] getMethodArgs()
: listArgs.toArray(Handy.NO_ARGS);
}

/**
* @return the map of specified injection keys and values
*/
public Map<String, String> getInjections()
{
return (Map<String, String>) values().getOrDefault("I", Collections.emptyMap());
}

@Override
public void validate()
{
Expand Down
Loading

0 comments on commit b85aacf

Please sign in to comment.