Skip to content

Commit 7ed3200

Browse files
committed
refactor: new way creating typed Result
1 parent 12bd8d4 commit 7ed3200

5 files changed

Lines changed: 145 additions & 112 deletions

File tree

.rubocop.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,7 @@ Lint/MissingSuper:
2626
Enabled: false
2727

2828
Naming/PredicatePrefix:
29+
Enabled: false
30+
31+
Metrics/AbcSize:
2932
Enabled: false

lib/rs/result.rb

Lines changed: 90 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,158 @@
11
# frozen_string_literal: true
22

3-
require_relative "option"
43
require_relative "result/version"
54

65
module Rs
76
# https://doc.rust-lang.org/std/result/index.html
87
# https://doc.rust-lang.org/std/result/enum.Result.html
98
# https://doc.rust-lang.org/src/core/result.rs.html
10-
module Result
11-
class ArgumentError < StandardError; end
9+
class Result
10+
class TypeError < StandardError
11+
def initialize(value_type, error_type)
12+
super("Expected #{value_type} or #{error_type} to be a Class")
13+
end
14+
end
15+
16+
class TypeNilClass < StandardError; end
17+
class TypeMismatch < StandardError; end
18+
class WrapNil < StandardError; end
1219
class UnwrapOnErr < StandardError; end
1320
class UnwrapErrOnOk < StandardError; end
14-
class TypeError < StandardError; end
1521

16-
def ok?
17-
@ok
22+
def is_ok
23+
is_a?(Ok)
1824
end
1925

20-
def ok_and
21-
if !@ok
22-
false
23-
elsif block_given?
24-
yield(@value)
25-
else
26-
@ok
27-
end
26+
def is_ok_and(&block)
27+
is_ok ? block.call(@value) : false
2828
end
2929

30-
def err?
31-
!@ok
30+
def is_err
31+
is_a?(Err)
3232
end
3333

34-
def err_and
35-
if @ok
36-
false
37-
elsif block_given?
38-
yield(@value)
39-
else
40-
!@ok
41-
end
34+
def is_err_and(&block)
35+
is_err ? block.call(@error) : false
4236
end
4337

4438
def ok
45-
if @ok
46-
Some.new(@value)
47-
else
48-
None.new
49-
end
39+
is_ok ? Some[@value.class] { @value } : None[@value_type]
5040
end
5141

5242
def err
53-
if !@ok
54-
Some.new(@value)
55-
else
56-
None.new
57-
end
43+
is_err ? Some[@error.class] { @error } : None[@error_type]
5844
end
5945

6046
def expect(msg)
61-
@ok ? @value : raise(UnwrapOnErr, "#{msg}: #{@value}")
47+
is_ok ? @value : raise(UnwrapOnErr.new("#{msg}: #{@error}"))
6248
end
6349

6450
def unwrap
65-
@ok ? @value : raise(UnwrapOnErr, "called `Result::unwrap()` on an `Err` value: #{@value}")
51+
is_ok ? @value : raise(UnwrapOnErr.new("called unwrap on an Err value: #{@error}"))
6652
end
6753

68-
def unwrap_or(default = nil)
69-
if @ok
70-
@value
71-
elsif block_given?
72-
yield
73-
else
74-
default
75-
end
54+
def unwrap_or(default)
55+
is_ok ? @value : default
7656
end
7757

7858
def unwrap_or_else(&block)
79-
@ok ? @value : block.call(@value)
59+
is_ok ? @value : block.call(@error)
8060
end
8161

8262
def expect_err(msg)
83-
@ok ? raise(UnwrapErrOnOk, "#{msg}: #{@value}") : @value
63+
is_ok ? raise(UnwrapErrOnOk.new("#{msg}: #{@value}")) : @error
8464
end
8565

8666
def unwrap_err
87-
@ok ? raise(UnwrapErrOnOk, "called `Result::unwrap_err()` on an `Ok` value: #{@value}") : @value
67+
is_ok ? raise(UnwrapErrOnOk.new("called unwrap_err() on an Ok value: #{@value}")) : @error
8868
end
8969
end
9070
end
9171

92-
class Ok
93-
include Rs::Result
94-
95-
attr_reader :type
96-
attr_reader :ok
97-
attr_reader :value
72+
class Ok < Rs::Result
73+
attr_accessor :error_type
9874

99-
private :value
75+
def self.[](value_type, error_type, &block)
76+
if !value_type.is_a?(Class) || !error_type.is_a?(Class)
77+
raise TypeError.new(value_type, error_type)
78+
end
10079

101-
def initialize(value, type: nil)
102-
if value == nil
103-
raise(Rs::Result::ArgumentError, "Ok value cannot be nil")
80+
if value_type == NilClass || error_type == NilClass
81+
raise TypeNilClass.new("Cannot create Ok[T, E] with [#{value_type}, #{error_type}]")
10482
end
10583

106-
if type
107-
if value.class != type
108-
raise(Rs::Result::TypeError, "Value type #{value.class} does not match #{type}")
109-
end
84+
value = block.call
85+
if !value.is_a?(value_type)
86+
raise TypeMismatch.new("Expected 'Ok[#{value_type}, #{error_type}] { #{value.inspect} }' block.call to be #{value_type}, not #{value.class}")
87+
end
11088

111-
@type = type
112-
else
113-
@type = value.class
89+
if value == nil
90+
raise WrapNil.new("Cannot create Ok[#{value_type}, #{error_type}] with nil")
11491
end
11592

116-
@ok = true
117-
@value = value
93+
ok_instance = Ok.new(value)
94+
ok_instance.error_type = error_type
95+
ok_instance
96+
end
97+
98+
def inspect
99+
"Ok[#{@value.class}, #{@error_type}] { #{@value.inspect} }"
118100
end
119101

120102
def ==(other)
121-
other.is_a?(Ok) && @type == other.type && @value == other.unwrap
103+
other.is_a?(Ok) && @value == other.unwrap && @value.class == other.unwrap.class && @error_type == other.error_type
104+
end
105+
106+
def initialize(value)
107+
if value == nil
108+
raise WrapNil.new("Cannot create Ok[T, E] with nil")
109+
end
110+
111+
@value = value
112+
@error_type = Class
122113
end
123114
end
124115

125-
class Err
126-
include Rs::Result
116+
class Err < Rs::Result
117+
attr_accessor :value_type
127118

128-
attr_reader :type
129-
attr_reader :ok
130-
attr_reader :value
119+
def self.[](value_type, error_type, &block)
120+
if !value_type.is_a?(Class) || !error_type.is_a?(Class)
121+
raise TypeError.new(value_type, error_type)
122+
end
131123

132-
private :value
124+
if value_type == NilClass || error_type == NilClass
125+
raise TypeNilClass.new("Cannot create Err[T, E] with [#{value_type}, #{error_type}]")
126+
end
133127

134-
def initialize(error, type: nil)
135-
if type
136-
if error.class != type
137-
raise(Rs::Result::TypeError, "Error type #{error.class} does not match #{type}")
138-
end
128+
error = block.call
129+
if !error.is_a?(error_type)
130+
raise TypeMismatch.new("Expected 'Err[#{value_type}, #{error_type}] { #{error.inspect} }' block.call to be #{error_type}, not #{error.class}")
131+
end
139132

140-
@type = type
141-
else
142-
@type = error.class
133+
if error == nil
134+
raise WrapNil.new("Cannot create Err[#{value_type}, #{error_type}] with nil")
143135
end
144136

145-
@ok = false
146-
@value = error
137+
error_instance = Err.new(error)
138+
error_instance.value_type = value_type
139+
error_instance
140+
end
141+
142+
def inspect
143+
"Err[#{@value_type}, #{@error.class}] { #{@error.inspect} }"
147144
end
148145

149146
def ==(other)
150-
other.is_a?(Err) && @type == other.type && @value == other.unwrap_err
147+
other.is_a?(Err) && @error == other.unwrap_err && @error.class == other.unwrap_err.class && @value_type == other.value_type
148+
end
149+
150+
def initialize(error)
151+
if error == nil
152+
raise WrapNil.new("Cannot create Err[T, E] with nil")
153+
end
154+
155+
@error = error
156+
@value_type = Class
151157
end
152158
end

lib/rs/result/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
module Rs
4-
module Result
4+
class Result
55
VERSION = "0.1.1"
66
end
77
end

0 commit comments

Comments
 (0)