-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
struct.cr
138 lines (134 loc) · 3.28 KB
/
struct.cr
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
# `Struct` is the base type of structs you create in your program.
# It is set as a struct's superstruct when you don't specify one:
#
# ```
# struct Foo # < Struct
# end
# ```
#
# Structs inherit from `Value` so they are allocated on the stack and passed
# by value. For this reason you should prefer using structs for immutable
# data types and/or stateless wrappers of other types.
#
# Mutable structs are still allowed, but code involving them must remember
# that passing a struct to a method actually passes a copy to it, so the
# method should return the modified struct:
#
# ```
# struct Mutable
# property value
#
# def initialize(@value : Int32)
# end
# end
#
# def change_bad(mutable)
# mutable.value = 2
# end
#
# def change_good(mutable)
# mutable.value = 2
# mutable
# end
#
# mut = Mutable.new 1
# change_bad(mut)
# mut.value # => 1
#
# mut = change_good(mut)
# mut.value # => 2
# ```
#
# The standard library provides a useful `record` macro that allows you to
# create immutable structs with some fields, similar to a `Tuple` but using
# names instead of indices.
struct Struct
# Returns `true` if this struct is equal to *other*.
#
# Both structs's instance vars are compared to each other. Thus, two
# structs are considered equal if each of their instance variables are
# equal. Subclasses should override this method to provide specific
# equality semantics.
#
# ```
# struct Point
# def initialize(@x : Int32, @y : Int32)
# end
# end
#
# p1 = Point.new 1, 2
# p2 = Point.new 1, 2
# p3 = Point.new 3, 4
#
# p1 == p2 # => true
# p1 == p3 # => false
# ```
def ==(other) : Bool
# TODO: This is a workaround for https://github.com/crystal-lang/crystal/issues/5249
if other.is_a?(self)
{% for ivar in @type.instance_vars %}
return false unless @{{ivar.id}} == other.@{{ivar.id}}
{% end %}
return true
else
return false
end
end
# See `Object#hash(hasher)`
def hash(hasher)
{% for ivar in @type.instance_vars %}
hasher = @{{ivar.id}}.hash(hasher)
{% end %}
hasher
end
# Appends this struct's name and instance variables names and values
# to the given IO.
#
# ```
# struct Point
# def initialize(@x : Int32, @y : Int32)
# end
# end
#
# p1 = Point.new 1, 2
# p1.to_s # "Point(@x=1, @y=2)"
# p1.inspect # "Point(@x=1, @y=2)"
# ```
def inspect(io : IO) : Nil
io << {{@type.name.id.stringify}} << '('
{% for ivar, i in @type.instance_vars %}
{% if i > 0 %}
io << ", "
{% end %}
io << "@{{ivar.id}}="
@{{ivar.id}}.inspect(io)
{% end %}
io << ')'
nil
end
def pretty_print(pp) : Nil
{% if @type.overrides?(Struct, "inspect") %}
pp.text inspect
{% else %}
prefix = "#{{{@type.name.id.stringify}}}("
pp.surround(prefix, ")", left_break: "", right_break: nil) do
{% for ivar, i in @type.instance_vars.map(&.name).sort %}
{% if i > 0 %}
pp.comma
{% end %}
pp.group do
pp.text "@{{ivar.id}}="
pp.nest do
pp.breakable ""
@{{ivar.id}}.pretty_print(pp)
end
end
{% end %}
end
{% end %}
end
# Same as `#inspect(io)`.
def to_s(io)
inspect(io)
end
end