summary refs log tree commit diff stats
path: root/lib/std/private/gitutils.nim
blob: 5bcd9e377bb6038c1df46242232b8a49103947fd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
##[
internal API for now, API subject to change
]##

# xxx move other git utilities here; candidate for stdlib.

import std/[os, osproc, strutils, tempfiles]

const commitHead* = "HEAD"

template retryCall*(maxRetry = 3, backoffDuration = 1.0, call: untyped): bool =
  ## Retry `call` up to `maxRetry` times with exponential backoff and initial
  ## duraton of `backoffDuration` seconds.
  ## This is in particular useful for network commands that can fail.
  runnableExamples:
    doAssert not retryCall(maxRetry = 2, backoffDuration = 0.1, false)
    var i = 0
    doAssert: retryCall(maxRetry = 3, backoffDuration = 0.1, (i.inc; i >= 3))
    doAssert retryCall(call = true)
  var result = false
  var t = backoffDuration
  for i in 0..<maxRetry:
    if call:
      result = true
      break
    if i == maxRetry - 1: break
    sleep(int(t * 1000))
    t = t * 2 # exponential backoff
  result

proc isGitRepo*(dir: string): bool =
  ## This command is used to get the relative path to the root of the repository.
  ## Using this, we can verify whether a folder is a git repository by checking
  ## whether the command success and if the output is empty.
  let (output, status) = execCmdEx("git rev-parse --show-cdup", workingDir = dir)
  # On Windows there will be a trailing newline on success, remove it.
  # The value of a successful call typically won't have a whitespace (it's
  # usually a series of ../), so we know that it's safe to unconditionally
  # remove trailing whitespaces from the result.
  result = status == 0 and output.strip() == ""

proc diffFiles*(path1, path2: string): tuple[output: string, same: bool] =
  ## Returns a human readable diff of files `path1`, `path2`, the exact form of
  ## which is implementation defined.
  # This could be customized, e.g. non-git diff with `diff -uNdr`, or with
  # git diff options (e.g. --color-moved, --word-diff).
  # in general, `git diff` has more options than `diff`.
  var status = 0
  (result.output, status) = execCmdEx("git diff --no-index $1 $2" % [path1.quoteShell, path2.quoteShell])
  doAssert (status == 0) or (status == 1)
  result.same = status == 0

proc diffStrings*(a, b: string): tuple[output: string, same: bool] =
  ## Returns a human readable diff of `a`, `b`, the exact form of which is
  ## implementation defined.
  ## See also `experimental.diff`.
  runnableExamples:
    let a = "ok1\nok2\nok3\n"
    let b = "ok1\nok2 alt\nok3\nok4\n"
    let (c, same) = diffStrings(a, b)
    doAssert not same
    let (c2, same2) = diffStrings(a, a)
    doAssert same2
  runnableExamples("-r:off"):
    let a = "ok1\nok2\nok3\n"
    let b = "ok1\nok2 alt\nok3\nok4\n"
    echo diffStrings(a, b).output

  template tmpFileImpl(prefix, str): auto =
    let path = genTempPath(prefix, "")
    writeFile(path, str)
    path
  let patha = tmpFileImpl("diffStrings_a_", a)
  let pathb = tmpFileImpl("diffStrings_b_", b)
  defer:
    removeFile(patha)
    removeFile(pathb)
  result = diffFiles(patha, pathb)
"p">()[high(arr1) - i .. high(arr1)] doAssert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i] block: var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] let list2 = list.rotatedLeft(1 ..< 9, 3) let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10] doAssert list.rotateLeft(1 ..< 9, 3) == 6 doAssert list == expected doAssert list2 == @expected var s0, s1, s2, s3, s4, s5 = "xxxabcdefgxxx" doAssert s0.rotateLeft(3 ..< 10, 3) == 7 doAssert s0 == "xxxdefgabcxxx" doAssert s1.rotateLeft(3 ..< 10, 2) == 8 doAssert s1 == "xxxcdefgabxxx" doAssert s2.rotateLeft(3 ..< 10, 4) == 6 doAssert s2 == "xxxefgabcdxxx" doAssert s3.rotateLeft(3 ..< 10, -3) == 6 doAssert s3 == "xxxefgabcdxxx" doAssert s4.rotateLeft(3 ..< 10, -10) == 6 doAssert s4 == "xxxefgabcdxxx" doAssert s5.rotateLeft(3 ..< 10, 11) == 6 doAssert s5 == "xxxefgabcdxxx" block product: doAssert product(newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input" doAssert product(@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input" doAssert product(@[@[1, 2]]) == @[@[1, 2]], "a simple case of one element" doAssert product(@[@[1, 2], @[3, 4]]) == @[@[2, 4], @[1, 4], @[2, 3], @[1, 3]], "two elements" doAssert product(@[@[1, 2], @[3, 4], @[5, 6]]) == @[@[2, 4, 6], @[1, 4, 6], @[2, 3, 6], @[1, 3, 6], @[2, 4, 5], @[1, 4, 5], @[2, 3, 5], @[1, 3, 5]], "three elements" doAssert product(@[@[1, 2], @[]]) == newSeq[seq[int]](), "two elements, but one empty" block lowerBound: doAssert lowerBound([1, 2, 4], 3, system.cmp[int]) == 2 doAssert lowerBound([1, 2, 2, 3], 4, system.cmp[int]) == 4 doAssert lowerBound([1, 2, 3, 10], 11) == 4 block upperBound: doAssert upperBound([1, 2, 4], 3, system.cmp[int]) == 2 doAssert upperBound([1, 2, 2, 3], 3, system.cmp[int]) == 4 doAssert upperBound([1, 2, 3, 5], 3) == 3 block fillEmptySeq: var s = newSeq[int]() s.fill(0) block testBinarySearch: var noData: seq[int] doAssert binarySearch(noData, 7) == -1 let oneData = @[1] doAssert binarySearch(oneData, 1) == 0 doAssert binarySearch(oneData, 7) == -1 let someData = @[1, 3, 4, 7] doAssert binarySearch(someData, 1) == 0 doAssert binarySearch(someData, 7) == 3 doAssert binarySearch(someData, -1) == -1 doAssert binarySearch(someData, 5) == -1 doAssert binarySearch(someData, 13) == -1 let moreData = @[1, 3, 5, 7, 4711] doAssert binarySearch(moreData, -1) == -1 doAssert binarySearch(moreData, 1) == 0 doAssert binarySearch(moreData, 5) == 2 doAssert binarySearch(moreData, 6) == -1 doAssert binarySearch(moreData, 4711) == 4 doAssert binarySearch(moreData, 4712) == -1 # merge proc main() = block: var x = @[1, 7, 8, 11, 21, 33, 45, 99] var y = @[6, 7, 9, 12, 57, 66] var merged: seq[int] merged.merge(x, y) doAssert merged.isSorted doAssert merged == sorted(x & y) block: var x = @[111, 88, 76, 56, 45, 31, 22, 19, 11, 3] var y = @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13] var merged: seq[int] merged.merge(x, y, (x, y) => -system.cmp(x, y)) doAssert merged.isSorted((x, y) => -system.cmp(x, y)) doAssert merged == sorted(x & y, SortOrder.Descending) block: var x: seq[int] = @[] var y = @[1] var merged: seq[int] merged.merge(x, y) doAssert merged.isSorted doAssert merged.isSorted(SortOrder.Descending) doAssert merged == @[1] block: var x = [1, 3, 5, 5, 7] var y: seq[int] = @[] var merged: seq[int] merged.merge(x, y) doAssert merged.isSorted doAssert merged == @x block: var x = [1, 3, 5, 5, 7] var y: seq[int] = @[] var merged: seq[int] = @[1, 2, 3, 5, 6, 56, 99, 2, 34] merged.merge(x, y) doAssert merged == @[1, 2, 3, 5, 6, 56, 99, 2, 34, 1, 3, 5, 5, 7] block: var x: array[0, int] var y = [1, 4, 6, 7, 9] var merged: seq[int] merged.merge(x, y) doAssert merged.isSorted doAssert merged == @y block: var x: array[0, int] var y: array[0, int] var merged: seq[int] merged.merge(x, y) doAssert merged.isSorted doAssert merged.len == 0 block: var x: array[0, int] var y: array[0, int] var merged: seq[int] = @[99, 99, 99] merged.setLen(0) merged.merge(x, y) doAssert merged.isSorted doAssert merged.len == 0 block: var x: seq[int] var y: seq[int] var merged: seq[int] merged.merge(x, y) doAssert merged.isSorted doAssert merged.len == 0 block: type Record = object id: int proc r(id: int): Record = Record(id: id) proc cmp(x, y: Record): int = if x.id == y.id: return 0 if x.id < y.id: return -1 result = 1 var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] var merged: seq[Record] = @[] merged.merge(x, y, cmp) doAssert merged.isSorted(cmp) doAssert merged.len == 12 block: type Record = object id: int proc r(id: int): Record = Record(id: id) proc ascendingCmp(x, y: Record): int = if x.id == y.id: return 0 if x.id < y.id: return -1 result = 1 proc descendingCmp(x, y: Record): int = if x.id == y.id: return 0 if x.id < y.id: return 1 result = -1 var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] var merged: seq[Record] merged.setLen(0) merged.merge(x, y, ascendingCmp) doAssert merged.isSorted(ascendingCmp) doAssert merged == sorted(x & y, ascendingCmp) reverse(x) reverse(y) merged.setLen(0) merged.merge(x, y, descendingCmp) doAssert merged.isSorted(descendingCmp) doAssert merged == sorted(x & y, ascendingCmp, SortOrder.Descending) reverse(x) reverse(y) merged.setLen(0) merged.merge(x, y, proc (x, y: Record): int = -descendingCmp(x, y)) doAssert merged.isSorted(proc (x, y: Record): int = -descendingCmp(x, y)) doAssert merged == sorted(x & y, ascendingCmp) var x: seq[(int, int)] x.merge([(1,1)], [(1,2)], (a,b) => a[0] - b[0]) doAssert x == @[(1, 1), (1, 2)] static: main() main()