I don’t want to call this library officially “deprecated”, but I haven’t
been paying as much attention to the code quality and/or in recent times. I
think everything up until version 0.14.4
can be considered “mostly
reliable” - so if you are looking to use this library, I would advise you
to pin dependency to this version.
Maybe in the future I will return to working on this library - for now I don’t think I have the energy required to constantly fight nim documentation generator, write unit testing framework and test runner and so on.
Collection of miscellaneous helper algorithms and types. Over time this library accumulated a lot of functionality, not all of this is necessary and almost everything was made on case-by-case basis instead of being designed at once.
Most useful parts of the library are probably
hmisc/other/oswrap
- wrapper for thestd/os
but with support for properdisctinct
typeshmisc/other/hshell
- shell command execution builderhmisc/core/gold
orhmisc/core/all
- these contain my import-everywhere templates, procedures and macros.
In addition to the general helpers, the library also contains forks of some
of the fusion modules - specifically hmisc/types/hmap
is a fork of
fusion/btreetables
with saner type naming (no more type clashes all over
the place) and couple helper routines. I also copied fusion/matching
back, since fusion can be considered effective dead and deprecated - not
tagged (at the time of writing requires fusion
still pulls 9-month old
version, that contains none of the fixes that was made to pattern
matching after that. For more details see forum thread - not especially
interesting, but if you want more context …). Right now it is a simple
copy, in the future I will clean up the implementation since I’ve got
several ideas on how it can be improved. DSL will be cleaned up as well,
right now it is really overloaded.
nimble install hmisc
Rust-like iflet. Get value from Option[T]
only if it contains
something, otherwise execute else
.
import options
import hmisc/macros/iflet
iflet (val = some(12)):
echo typeof val
iflet (val = none(int)):
echo "???"
else:
echo "no value"
int no value
hmisc/algo/halgorithm
documentation
- predicates
allOfIt
anyOfIt
noneOfIt
==
for Option-Val comparison
- working with sequences
maxIt
disjointIter
last
- working with strings
joinw
,joinq
,joinl
,joinkv
- join on whitespaces, whitespace + quote each string, newlines or key-value pairs. Mostly useful instrformat.&
- can write{somevar.join()}
instead of{somevar.join(\"\\n\")}
wrap
- wrap strings in delimiters. Has convinience overload for.wrap("()")
that automatically determines starting/ending wrapper strings.- Multiple overloads for
join
andstartsWith
enclosedIn
- check if string is wrapped in delimitersdedent
- decease indentation for multiline stringcamelSplit
- split string as camel case identifierabbrevCamel
- camelCase abbreviation search.- Several variations of
dropPrefix
,addPrefix
,startsWith
,addSuffix
for less common use cases - Filtering sequence of strings by prefix
- Dropping subsequence in strings
- Finding common prefix in sequence of strings
- other
ifSomeIt
- same asopt.isSome() and (let it = opt.get(); predicate)
testEq
- compare two objects. If they are different print first mismatching line in their string representation.assertEq
- compare objects usingtestEq
, raise on failed comparison.
import hmisc/algo/halgorithm, std/strformat
let v = @["w234", "333"]
echo ": ", &"{v.joinq()}"
block:
echo "-- withIt --"
let immutable = (a: 12, b: 12)
echo immutable.withIt do:
it.a = 909
block:
echo "-- withResIt --"
let immutable = (a: 12, b: "eee")
echo immutable.withResIt do:
it.a += 999
$it.a & it.b
block:
echo "-- join* --"
echo {1 : "22", 3: "333"}.joinkv().join()
block:
echo "-- abbrevCamel --"
echo abbrevCamel("AA", @["ABA", "AZZ", "A)"])
: "w234" "333" -- withIt -- (a: 909, b: 12) -- withResIt -- 1011eee -- join* -- 0 = (1, "22") 1 = (3, "333") -- abbrevCamel -- @["ABA"]
hmisc/algo/hseqdistance
documentation
Fuzzy string matching and generic longest common subsequece implementation
longestCommonSubsequence
- generic implementation of LCS algorithm forseq[T]
fuzzyMatch
- weighted sequence fuzzy match. Compare each element in the sequence to pattern and assign similarity score. Should behave similarly tofzf
or sublime text. Reimplementation of ‘Reverse engineering subtime text’s fuzzy match’. I haven’t used it in any interactive applications as of yet, but there are some unit tests. It has generic implementation and somewhat annoying to use, but provides very flexible interface, allowing to completely customize how fuzzy matching is performed.
import hmisc/doc_examples
echo "# ~~~~ leading / ~~~~ #\n|"
matchTest "//hell.txt", "/nice/we/hell.txt":
if other[matches[0]] == '/':
1000 # high cost if have exact match with starting /
else:
matches.sum()
echo "|\n# ~~~~ no leading / ~~~~ #\n|"
matchTest "nicehell.txt", "/nice/we/hell.txt":
if other[matches[0]] == '/':
1000
else:
matches.sum()
# ~~~~ leading / ~~~~ # | input: /nice/we/hell.txt //hell.txt :1000 match: / / hell.txt | # ~~~~ no leading / ~~~~ # | input: /nice/we/hell.txt nicehell.txt :113 match: nic e hell.txt
deduplicateIt
mapPairs
mapIt
for types that implementpairs
iterator, oritems
that return tuple, or sequence of tuples. Inject index of the item,lhs
(first element) andrhs
(second element). Should correctly handle{.requiresinit.}
fields.
mapItBFStoSeq
- iterate over tree in BFS order, store mapping result in sequence.
iterateItBFS
- iterate over tree in BFS order
iterateItDFS
- iterate over tree in DFS order. Uses iterative DFS instead of recursive call.
mapItDFS
mapIt
for converting trees in DFS order
Easier manipulation of colored strings in terminal. Support splitting
regular strin in same-color chunks, finding ‘visible’ length of the
string (as printed in terminal). Helper functions like toYellow()
or
toRed()
to make creation of the colored strings simpler. All
attributes from terminal
module are supported (fg/bg colors and
modifiers).
Provides two types for colored text - ColoredString
(string +
styling) and ColoredRune
(unicode rune + styling).
hshell
and oswrap
modules provide more strictly typed wrappers for
tasks that are usually performed using simple string concatenations.
You get better static safety guarantees (not possible to pass relative
path to function expecting absolute one) and less headaches related to
correct quoting/CLI command syntax at the expense of little more
verbose code.
oswrap
is a 1:1
mapping of std/os
and is expected to have all
functions reimplemented (wrapped).
hshell
also treats non-zero return codes as exceptions, so you can
just execute shell commands without endless checks for code != 0:
echo "oh no!"
. This can be turned off, but works by default, so when
writing let (output, err, _) = runShell("someCommand")
you will be
sure that failures won’t be silently ignored.
Wrapper on top of os
and nimscsrip
that allows to use the same
code on c
and nimscript
targets. Some helper templates/functions
are introduced. Provide distinct string for files/directories - e.g.
RelDIr = distinct string
as well as overloads for almost all
functions in os
module.
NOTE it is expected to be imported instead of os
module -
functions without arguments were update to use distinct
types too,
so if two modules are imported togetether frequent type clashes are
expected.
mkDir
,getEnv
,delEnv
,toExe
,listDirs
,rmFile
,mkDir
,mvFile
,cpFile
,cpDir
,cd
,cwd
- default file/directory manipulation functions- ~ (prefix tilda) prefix operator to get path relative to home
directory. Same as
getHomeDir() / path
&&
join shell command strings with correct spacingwithDir
- temporarily set directory for bodywithEnv
- temporarily set environment variables for body
hmisc/other/hshell
documentation
Helper functions for running shell commands - reduce need for string
concatenation for shell - Cmd
object supports adding
commands/flags/options/subcommands/arguments while deferring
conversion to string as long as possible and taking care of correct
syntax (correct dashes for X11
CLI tools (always single prefix
dash), key-value separators (nim tooling uses :
, GNU is most likely
to expect =
or spaces)).
Possible use case: Imagine you need to write a script that launches new docker container, mounts some folders, copy files over, and perform some nontrivial commands inside container (and command is not predetermined - it is also has to be built in advance).
Regular approach would be to cobble together one giant string that will
then be executed via startProcess
. You need to then check for return
code, and hope that you haven’t messed up quoting, argument syntax for
particular command and so on (nim tooling uses :
, GNU - =
and so on).
hshell
hopefully provides solution to most of the usability of command
line programs - you no longer need to worry about correct spacing, quoting
and other stuff like that. Instead, you just build AST for command to be
executed, using set of convenient operators and functions.
let cmd = shCmd("nimble", "install")
# Nice side effect - you can now comment on different flags and use
# checks/loops without worrying about correct
# spacing/concatnation/prefixes etc.
let doCleanup = true
let dockerCmd = shCmd("docker").withIt do:
it.cmd "run" # Add subcommand
it - "i"
it - "t"
if doCleanup:
it - "rm" # Remove container after test execution
it - ("v", "/tmp/tmp-mount:/project") # Key-value pair
it.arg "nim-base"
it.arg "sh"
it - "c"
it.expr:
shAnd:
shCmd(cd, "/project/main")
cmd # Can easily build complicated commands from variables
NOTE: no special DSL syntax is introduced, just couple of overlads for
common use cases (-
proc for flags/options)
runShell
Raise exception when command has exited with non-zero code (because you will be checking return code anyway), get stderr and stdout separately. Uses fallbackexec
andgorgeEx
on nimscript targets and tries to emulate compiled behaviour as close as possible (respect execution flags).iterstdout
- iterate each line for executed program’s stdoutexecShell
- execute shell command, redirect output into parent streams.
NOTE: You might consider this module a ‘shell program wrapper’. It was
created to make using external processes from your code easier and safer.
No need to check return codes all the time, think about quoting, correct
arguments and so on. Thus said - it is quite difficult to wrap all
complixity of the command line interfaces, even with quite sophisticated
logic. Several escape hatches are present, to still pass almost arbitrary
strings for shell execution. First: ShellExpr
- thin wrapper, distinct
string
. Second is raw()
function for setting command line arguments.
Most of the features in this library were implemented on do-it-when-I-need-it basis. Some of them are tested quite extensively (sequence and tree mappings, colored strings), but more unit test are always welcome.