# The `Nil` type has only one possible value: `nil`.
#
# `nil` is commonly used to represent the absence of a value.
# For example, `String#index` returns the position of the character or `nil` if it's not
# in the string:
#
# ```
# str = "Hello world"
# str.index 'e' # => 1
# str.index 'a' # => nil
# ```
#
# In the above example, trying to invoke a method on the returned value will
# give a compile time error unless both `Int32` and `Nil` define that method:
#
# ```
# str = "Hello world"
# idx = str.index 'e'
# idx + 1 # Error: undefined method '+' for Nil
# ```
#
# The language and the standard library provide short, readable, easy ways to deal with `nil`,
# such as `Object#try` and `Object#not_nil!`:
#
# ```
# str = "Hello world"
#
# # The index of 'e' in str or 0 if not found
# idx1 = str.index('e') || 0
#
# idx2 = str.index('a')
# if idx2
#   # Compiles: idx2 can't be nil here
#   idx2 + 1
# end
#
# # Tell the compiler that we are sure the returned
# # value is not nil: raises a runtime exception
# # if our assumption doesn't hold.
# idx3 = str.index('o').not_nil!
# ```
#
# See [`Nil` literal](https://crystal-lang.org/reference/syntax_and_semantics/literals/nil.html) in the language reference.
struct Nil
  # Returns `0_u64`. Even though `Nil` is not a `Reference` type, it is usually
  # mixed with them to form nilable types so it's useful to have an
  # object id for `nil`.
  def object_id : UInt64
    0_u64
  end

  # :nodoc:
  def crystal_type_id
    0
  end

  # Returns `true`: `Nil` has only one singleton value: `nil`.
  def ==(other : Nil)
    true
  end

  # Returns `true`: `Nil` has only one singleton value: `nil`.
  def same?(other : Nil)
    true
  end

  # Returns `false`.
  def same?(other : Reference) : Bool
    false
  end

  # See `Object#hash(hasher)`
  def hash(hasher)
    hasher.nil
  end

  # Returns an empty string.
  def to_s : String
    ""
  end

  # Doesn't write anything to the given `IO`.
  def to_s(io : IO) : Nil
    # Nothing to do
  end

  # Returns `"nil"`.
  def inspect : String
    "nil"
  end

  # Writes `"nil"` to the given `IO`.
  def inspect(io : IO) : Nil
    io << "nil"
  end

  # Doesn't yield to the block.
  #
  # See also: `Object#try`.
  def try(&block)
    self
  end

  # Raises `NilAssertionError`.
  #
  # If *message* is given, it is forwarded as error message of `NilAssertionError`.
  #
  # See also: `Object#not_nil!`.
  def not_nil!(message = nil) : NoReturn
    if message
      raise NilAssertionError.new(message)
    else
      raise NilAssertionError.new
    end
  end

  # Returns `self`.
  # This method enables to call the `presence` method (see `String#presence`) on a union with `Nil`.
  # The idea is to return `nil` when the value is `nil` or empty.
  #
  # ```
  # config = {"empty" => ""}
  # config["empty"]?.presence   # => nil
  # config["missing"]?.presence # => nil
  # ```
  def presence : Nil
    self
  end

  def clone
    self
  end
end
