nandi/rustnimpublic Fork 0
b66da6b1c3e50bd75a8d5010cb222724b5ce6c13
Commits
Clone
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 · on b66da6b1c3e50bd75a8d5010cb222724b5ce6c13 · nandithebull · 6h ago
prelude.nim · 150 lines · 5.2 KBNim Blame HistoryRaw
  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
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
## rustnim prelude — emitted at the top of every generated module.
##
## Everything here exists to make Nim's observable behaviour match Rust's
## exactly. Where the two languages already agree (signed `shr` is arithmetic
## in both; fixed-width unsigned arithmetic wraps in both; integer `div`/`mod`
## truncate toward zero in both) there is deliberately nothing here: the
## operator is mapped directly and no helper is involved.

import std/[unicode, strutils]

type
  RustPanic* = object of CatchableError

  Option*[T] = object
    case has*: bool
    of true: val*: T
    of false: discard

  Result*[T, E] = object
    case ok*: bool
    of true: val*: T
    of false: err*: E

proc rsPanic*(msg: string) {.noreturn.} =
  raise newException(RustPanic, msg)

proc rsSome*[T](v: T): Option[T] = Option[T](has: true, val: v)
proc rsNone*[T](): Option[T] = Option[T](has: false)
proc rsOk*[T, E](v: T): Result[T, E] = Result[T, E](ok: true, val: v)
proc rsErr*[T, E](e: E): Result[T, E] = Result[T, E](ok: false, err: e)

proc rsOkOr*[T, E](o: Option[T], e: E): Result[T, E] =
  if o.has: Result[T, E](ok: true, val: o.val) else: Result[T, E](ok: false, err: e)

proc unwrapOr*[T](o: Option[T], d: T): T =
  if o.has: o.val else: d
proc unwrapOr*[T, E](r: Result[T, E], d: T): T =
  if r.ok: r.val else: d

proc unwrap*[T](o: Option[T]): T =
  if not o.has: rsPanic("called `Option::unwrap()` on a `None` value")
  o.val
proc unwrap*[T, E](r: Result[T, E]): T =
  if not r.ok: rsPanic("called `Result::unwrap()` on an `Err` value")
  r.val

# ---------------------------------------------------------------------------
# Display / Debug.
#
# Rust's `{}` and `{:?}` are two distinct formats and they differ for floats,
# strings, chars and sequences. Nim's `$` matches neither consistently, so both
# are implemented here rather than approximated with `$`.
# ---------------------------------------------------------------------------

proc rsDisplay*(x: SomeInteger): string = $x
proc rsDebug*(x: SomeInteger): string = $x
proc rsDisplay*(x: bool): string = $x
proc rsDebug*(x: bool): string = $x

proc rsFloatStr(x: float64, debug: bool): string =
  ## Nim and Rust both print the shortest round-tripping decimal, but they
  ## spell the result differently in three places.
  if x != x: return "NaN"
  if x == Inf: return "inf"
  if x == -Inf: return "-inf"
  result = $x
  result = result.replace("e+", "e")        # Nim `1e+21`, Rust `1e21`
  if not debug and result.endsWith(".0"):   # Rust Display drops a bare `.0`
    result.setLen(result.len - 2)

proc rsDisplay*(x: float32 | float64): string = rsFloatStr(float64(x), false)
proc rsDebug*(x: float32 | float64): string = rsFloatStr(float64(x), true)

proc rsDisplay*(x: string): string = x
proc rsDebug*(x: string): string =
  result = "\""
  for c in x:
    case c
    of '"': result.add("\\\"")
    of '\\': result.add("\\\\")
    of '\n': result.add("\\n")
    of '\t': result.add("\\t")
    of '\r': result.add("\\r")
    else: result.add(c)
  result.add("\"")

proc rsDisplay*(x: Rune): string = $x
proc rsDebug*(x: Rune): string =
  case $x
  of "'": "'\\''"
  of "\\": "'\\\\'"
  of "\n": "'\\n'"
  of "\t": "'\\t'"
  of "\r": "'\\r'"
  else: "'" & $x & "'"

proc rsDebug*[T](x: seq[T] | openArray[T]): string =
  result = "["
  for i in 0 ..< x.len:
    if i > 0: result.add(", ")
    result.add(rsDebug(x[i]))
  result.add("]")

proc rsDebug*[T](o: Option[T]): string =
  if o.has: "Some(" & rsDebug(o.val) & ")" else: "None"
proc rsDebug*[T, E](r: Result[T, E]): string =
  if r.ok: "Ok(" & rsDebug(r.val) & ")" else: "Err(" & rsDebug(r.err) & ")"

# ---------------------------------------------------------------------------
# Radix formats: `{:x}`, `{:X}`, `{:b}`, `{:o}`, with Rust's width/zero-fill.
# Rust formats the *two's-complement bit pattern*, so a negative i8 prints as
# `ff`, not `-1`. `toHex` on the unsigned view of the same width reproduces it.
# ---------------------------------------------------------------------------

proc rsRadix*[T: SomeInteger](x: T, base: int, upper: bool): string =
  var v: uint64 =
    when T is SomeSignedInt:
      # Sign-extend then mask to the type's own width, so the printed bit
      # pattern is the Rust one for this exact integer type.
      cast[uint64](int64(x)) and (if sizeof(T) == 8: high(uint64) else: (1'u64 shl (sizeof(T) * 8)) - 1)
    else:
      uint64(x)
  if v == 0: return "0"
  const digits = "0123456789abcdef"
  while v > 0'u64:
    result.add(digits[int(v mod uint64(base))])
    v = v div uint64(base)
  for i in 0 ..< result.len div 2:
    swap(result[i], result[result.len - 1 - i])
  if upper: result = result.toUpperAscii()

proc rsPad*(s: string, width: int, zero: bool): string =
  if s.len >= width: return s
  let fill = width - s.len
  if zero and s.len > 0 and s[0] == '-':
    "-" & repeat('0', fill) & s[1 .. ^1]
  elif zero:
    repeat('0', fill) & s
  else:
    repeat(' ', fill) & s

proc rsBytes*(s: string): seq[uint8] =
  ## Rust's `str::as_bytes` is a view of the UTF-8 encoding; Nim's `string` is
  ## already those bytes, so this is a reinterpretation, not a conversion.
  result = newSeq[uint8](s.len)
  for i in 0 ..< s.len: result[i] = uint8(s[i])

proc newSeqWith*[T](n: int, v: T): seq[T] =
  result = newSeq[T](n)
  for i in 0 ..< n: result[i] = v