Coverage for src / pyfplib / result.py: 40%

86 statements  

« prev     ^ index     » next       coverage.py v7.13.2, created at 2026-02-04 23:48 +0300

1""" """ 

2 

3__author__ = "Comet11x <>" 

4__copyright__ = "Copyright 2026, Comet11x" 

5__license__ = "MIT" 

6__version__ = "0.1.0" 

7 

8from typing import Callable, Generic, Optional, TypeVar, Union, cast 

9 

10from pyfplib.errors import ExpectedError, UnwrapError 

11from pyfplib.option import Nothing, Option, Some 

12 

13T = TypeVar("T") 

14E = TypeVar("E") 

15R = TypeVar("R") 

16U = TypeVar("U") 

17 

18 

19class Result(Generic[T, E]): 

20 __match_args__ = ("value",) 

21 

22 def __init__(self, *, value: Optional[T] = None, error: Optional[E] = None): 

23 if error is None: 23 ↛ 27line 23 didn't jump to line 27 because the condition on line 23 was always true

24 self.__value = value 

25 self.__is_ok = True 

26 else: 

27 self.__value = error 

28 self.__is_ok = False 

29 

30 @property 

31 def value(self) -> Union[T, E]: 

32 """Returns a raw value.""" 

33 return self.__value 

34 

35 def is_ok(self) -> bool: 

36 """Returns True if the option is Ok[T].""" 

37 return self.__is_ok 

38 

39 def is_err(self) -> bool: 

40 """Returns True if the option is Err[T].""" 

41 return not self.__is_ok 

42 

43 def if_ok(self, fn: Callable[[T], None]) -> "Result[T, E]": 

44 """\ 

45 Calls fn function and returns itself. 

46 The fn function will be called if this option is Ok[T]. 

47 """ 

48 if self.is_ok(): 

49 fn(self.__value) 

50 return self 

51 

52 def if_err(self, fn: Callable[[E], None]) -> "Result[T, E]": 

53 """\ 

54 Calls fn function and returns itself. 

55 The fn function will be called if this option is Err[E]. 

56 """ 

57 if self.is_err(): 

58 fn(self.__value) 

59 return self 

60 

61 def ok(self) -> Option[T]: 

62 """\ 

63 Returns Some[T] if this option is Ok[T], 

64 otherwise it returns Nothing[T]. 

65 """ 

66 return Some(self.__value) if self.is_ok() else Nothing() 

67 

68 def err(self) -> Option[E]: 

69 """\ 

70 Returns Some[Exception] if this option is Err[E], 

71 otherwise it returns Nothing[Exception]. 

72 """ 

73 option: Option[Exception] 

74 if self.is_err(): 

75 option = Some[Exception](cast(Exception, self.__value)) 

76 else: 

77 option = Nothing[Exception]() 

78 return option 

79 

80 def unwrap(self) -> T: 

81 """ 

82 Extracts the contained value, raising UnwrapError if Err. 

83 

84 Raises: 

85 UnwrapError: When attempting to unwrap Err 

86 """ 

87 if self.is_err(): 

88 msg = "called `Result.unwrap()` on a `Err` value" 

89 raise UnwrapError(msg) 

90 return self.__value 

91 

92 def unwrap_err(self) -> E: 

93 """ 

94 Extracts the contained error, raising UnwrapError if Ok. 

95 

96 Raises: 

97 UnwrapError: When attempting to unwrap Ok 

98 """ 

99 if self.is_ok(): 

100 msg = "called `Result.unwrap_err()` on a `Ok` value" 

101 raise UnwrapError(msg) 

102 return self.__value 

103 

104 def expect(self, message: str) -> T: 

105 """Returns contained value or raises ExpectedError""" 

106 if self.is_err(): 

107 raise ExpectedError(message) 

108 return self.__value 

109 

110 def expect_err(self, message: str) -> E: 

111 """Returns contained value or raises ExpectedError""" 

112 if self.is_ok(): 

113 raise ExpectedError(message) 

114 return self.__value 

115 

116 def unwrap_or(self, value: T) -> T: 

117 """Returns contained value or provided default if Err.""" 

118 return self.__value if self.is_ok() is None else value 

119 

120 def unwrap_err_or(self, value: E) -> E: 

121 """Returns contained value or provided default if Ok.""" 

122 return self.__value if self.is_err() is None else value 

123 

124 def map(self, fn: Callable[[T], U]) -> "Result[U, E]": 

125 """Maps an Result[T, E] to Result[U, E]""" 

126 result: Result[U, E] 

127 if self.is_ok(): 

128 result = Ok[U, E](fn(cast(T, self.__value))) 

129 else: 

130 result = Err[U, E](cast(E, self.__value)) 

131 return result 

132 

133 def map_or(self, default: T, fn: Callable[[T], U]) -> "Result[U]": 

134 """Maps an Result[T] to Result[U]""" 

135 result: Result[U] 

136 if self.is_ok(): 

137 result = fn(self.__value) 

138 else: 

139 result = fn(default) 

140 return result 

141 

142 @staticmethod 

143 def try_call(fn: Callable[..., R], *args, **kwargs) -> "Result[R, E]": 

144 """Tries to call fn function and returns a result of the execution.""" 

145 try: 

146 ret: R = fn(*args, **kwargs) 

147 return Ok[R, E](ret) 

148 except Exception as err: 

149 return Err[R, E](err) 

150 

151 

152class Ok(Result[T, E]): 

153 """Result constructor.""" 

154 

155 def __init__(self, value: T): 

156 super().__init__(value=value) 

157 

158 

159class Err(Result[T, E]): 

160 """Result constructor.""" 

161 

162 def __init__(self, error: E): 

163 super().__init__(error=error)