diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/util.d | 197 | 
1 files changed, 194 insertions, 3 deletions
diff --git a/source/util.d b/source/util.d index 3624d88..0e3fc18 100644 --- a/source/util.d +++ b/source/util.d @@ -10,9 +10,16 @@ import std.path;  import std.conv : to; +/// MAXCOLS refers to terminal width  enum byte MAXCOLS = 80; +/** +  Print a wordwrapped string encapsulated by `ch` +Params: +    ch = ASCII character to create border +    s = string to print + */  void banner(const char ch, string s) {      string ruler;      byte i = 0; @@ -36,17 +43,54 @@ void banner(const char ch, string s) {  } -static auto getenv(string[string] base=null, string preface=null) { +/** +  Dump the parent shell runtime environment and convert it into an associative +  array + +Params: +    base = use an existing mapping as the base environment +    preface = command to execute prior to dumping the environment + +Returns: +    an associative array containing the runtime environment + +Example: +--- +import std.stdio; +import util; + +void main() +{ +    string exfile = "example.sh"; +    File(exfile, "w+").write("export EXAMPLE_FILE=parsed\n"); +    scope(exit) exfile.remove; + +    auto myenv = getenv(); +    myenv["EXAMPLE"] = "works"; + +    auto myenv2 = getenv(myenv); +    writeln(myenv2["EXAMPLE"]); + +    auto myenv3 = getenv(myenv2, "source example.sh"); +    writeln(myenv3["EXAMPLE"]); +    writeln(myenv3["EXAMPLE_FILE"]); +} +--- + */ +string[string] getenv(string[string] base=null, string preface=null) {      const char delim = '=';      char delim_line = '\n';      string[string] env;      string cmd = "env"; +    /// Under GNU we have the option to use nul-terminated strings, which means +    /// we can safely parse awful pairs generated by `env-modules`      version (linux) {          cmd ~= " -0";          delim_line = '\0';      } +    /// Untested      version (Windows) {          cmd = "set";          delim_line = "\r\n"; @@ -78,10 +122,54 @@ static auto getenv(string[string] base=null, string preface=null) {  } +/** +  Produce a single-quoted string + +Params: +    s = string to quote + +Returns: +    single-quoted string + +Example: +--- +import std.stdio; +import util; + +void main() +{ +    writeln(safe_spec("single-quoted")); +    // 'single-quoted' +} +--- + */  string safe_spec(string s) {      return "'" ~ s ~ "'";  } + +/** +  Produces conda/pip compatible installation arguments + +Params: +    specs = array of string arguments + +Returns: +    string of single-quoted arguments + +Example: +--- +import std.stdio; +import util; + +void main() +{ +    string[] arguments = ["a", "b", "c"]; +    writeln(safe_install(arguments)); +    // 'a' 'b' 'c' +} +--- + */  string safe_install(string[] specs) {      string[] result;      foreach (record; specs) { @@ -90,6 +178,30 @@ string safe_install(string[] specs) {      return result.join(" ");  } + +/** +  Produces `conda`/`pip` compatible installation arguments by splitting on white +  space + +  Params: +      specs = a string containing arguments + +  Returns: +      string of single quoted arguments + +  Example: +  --- +  import std.stdio; +  import util; + +  void main() +  { +      string arguments = "a b c"; +      writeln(safe_install(arguments)); +      // 'a' 'b' 'c' +  } +  --- + */  string safe_install(string specs) {      string[] result;      foreach (record; specs.split(" ")) { @@ -99,7 +211,19 @@ string safe_install(string specs) {  } +/** +  pytest emits invalid junit, so this rewrites the local configuration +  file (i.e. `setup.cfg`, `pytest.ini`, etc) to include the proper +  `junit_family` settings. + +Params: +    filename = path to configuration file + +Returns: +    new configuration file contents as string + */  string pytest_xunit2(string filename) { +    // Generate the requested file if need be      if (!filename.exists) {          auto dummy = File(filename, "w+");          dummy.write(""); @@ -139,6 +263,7 @@ string pytest_xunit2(string filename) {          return data ~ format("\n%s\n%s\n", section, cfgitem);      } +    // figure out when/where we should write our revisions to the config      foreach (rec; splitLines(data)) {          if (!has_section) {              break; @@ -163,6 +288,16 @@ string pytest_xunit2(string filename) {  } +/** +  Find all occurences of character in a string + +Params: +    s = string to read +    ch = character to find + +Returns: +    array of offsets +  */  ulong[] indexOfAll(string s, char ch) {      ulong[] result;      for (ulong i = 0; i < s.length; i++) { @@ -174,6 +309,7 @@ ulong[] indexOfAll(string s, char ch) {  } +/// Unused  string expander(string[string] aa, string name, char delim = '$') {      string s = aa[name].dup;      ulong[] needles = indexOfAll(s, delim); @@ -200,25 +336,57 @@ string expander(string[string] aa, string name, char delim = '$') {  } + +/** +  Perform variable interpolation on a string given a named environment + +Params: +    aa = assoc. array to use (i.e. runtime environment) +    str = string to scan for variables +    delim = character to trigger parsing variable + +Returns: +    string with variables replaced + +Note: +    When a variable cannot be mapped the variable text in the string is not +    modified. + +Example: +--- +import std.stdio; +import util; + +void main() +{ +    string[string] aa = ["my_var": "example"]; +    string my_str = "This is the ${my_var}."; +    writeln(interpolate(aa, my_str)); +} +--- +  */  string interpolate(string[string]aa, string str, char delim = '$') {      import std.ascii;      string s = str.dup;      ulong[] needles = indexOfAll(s, delim);      string[] found; +    // scan any indicies we've found      foreach (needle; needles) {          string tmp = ""; +        // trigger variable parsing on delimiter          for (ulong i = needle; i < s.length; i++) {              if (s[i] == delim) continue; -            else if (s[i] == '{' || s[i] == '}') +            else if (s[i] == '{' || s[i] == '}')    // ${} also supported                  continue; -            else if (!s[i].isAlphaNum && s[i] != '_' ) +            else if (!s[i].isAlphaNum && s[i] != '_') // unusable, die                  break;              tmp ~= s[i];          }          found ~= tmp;      } +    // rewrite string with substitutions      foreach (match; found) {          foreach (pair; aa.byPair) {              if (pair.key != match) @@ -232,6 +400,29 @@ string interpolate(string[string]aa, string str, char delim = '$') {  } +/** +  Produce a short/compact version + +Params: +    vrs = version string + +Returns: +    shortened version string + +Example: +--- +import std.stdio; +import util; + +void main() +{ +    writeln(short_version("3.6.8")); +    // 36 +    writeln(short_version("2.7.66")); +    // 27 +} +--- +  */  string short_version(string vrs) {      string tmp = vrs.dup;      tmp = tmp.replace(".", "");  | 
