|
1 | 1 | # frozen_string_literal: true |
2 | 2 |
|
3 | | -require_relative "option" |
4 | 3 | require_relative "result/version" |
5 | 4 |
|
6 | 5 | module Rs |
7 | 6 | # https://doc.rust-lang.org/std/result/index.html |
8 | 7 | # https://doc.rust-lang.org/std/result/enum.Result.html |
9 | 8 | # 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 |
12 | 19 | class UnwrapOnErr < StandardError; end |
13 | 20 | class UnwrapErrOnOk < StandardError; end |
14 | | - class TypeError < StandardError; end |
15 | 21 |
|
16 | | - def ok? |
17 | | - @ok |
| 22 | + def is_ok |
| 23 | + is_a?(Ok) |
18 | 24 | end |
19 | 25 |
|
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 |
28 | 28 | end |
29 | 29 |
|
30 | | - def err? |
31 | | - !@ok |
| 30 | + def is_err |
| 31 | + is_a?(Err) |
32 | 32 | end |
33 | 33 |
|
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 |
42 | 36 | end |
43 | 37 |
|
44 | 38 | 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] |
50 | 40 | end |
51 | 41 |
|
52 | 42 | 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] |
58 | 44 | end |
59 | 45 |
|
60 | 46 | def expect(msg) |
61 | | - @ok ? @value : raise(UnwrapOnErr, "#{msg}: #{@value}") |
| 47 | + is_ok ? @value : raise(UnwrapOnErr.new("#{msg}: #{@error}")) |
62 | 48 | end |
63 | 49 |
|
64 | 50 | 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}")) |
66 | 52 | end |
67 | 53 |
|
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 |
76 | 56 | end |
77 | 57 |
|
78 | 58 | def unwrap_or_else(&block) |
79 | | - @ok ? @value : block.call(@value) |
| 59 | + is_ok ? @value : block.call(@error) |
80 | 60 | end |
81 | 61 |
|
82 | 62 | def expect_err(msg) |
83 | | - @ok ? raise(UnwrapErrOnOk, "#{msg}: #{@value}") : @value |
| 63 | + is_ok ? raise(UnwrapErrOnOk.new("#{msg}: #{@value}")) : @error |
84 | 64 | end |
85 | 65 |
|
86 | 66 | 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 |
88 | 68 | end |
89 | 69 | end |
90 | 70 | end |
91 | 71 |
|
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 |
98 | 74 |
|
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 |
100 | 79 |
|
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}]") |
104 | 82 | end |
105 | 83 |
|
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 |
110 | 88 |
|
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") |
114 | 91 | end |
115 | 92 |
|
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} }" |
118 | 100 | end |
119 | 101 |
|
120 | 102 | 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 |
122 | 113 | end |
123 | 114 | end |
124 | 115 |
|
125 | | -class Err |
126 | | - include Rs::Result |
| 116 | +class Err < Rs::Result |
| 117 | + attr_accessor :value_type |
127 | 118 |
|
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 |
131 | 123 |
|
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 |
133 | 127 |
|
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 |
139 | 132 |
|
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") |
143 | 135 | end |
144 | 136 |
|
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} }" |
147 | 144 | end |
148 | 145 |
|
149 | 146 | 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 |
151 | 157 | end |
152 | 158 | end |
0 commit comments