Coverage for src/monadc/option/some.py: 96%

1 from typing import Callable, Union, Any, Optional, Iterator

2 from .option import Option, T, U, V, E

3 from .nil import Nil

4

5

6 class Some(Option[T]):

7

8 __match_args__ = ("_value",)

9

10 def __init__(self, value: T) -> None:

11 if value is None:

12 raise ValueError("Cannot create Some with None value")

13 self._value = value

14

15 # ========================================

16 # Rust / Scala Common API

17 # ========================================

18

19 def is_defined(self) -> bool:

20 return True

21

22 def is_empty(self) -> bool:

23 return False

24

25 def map(self, func: Callable[[T], U]) -> Option[U]:

26 result = func(self._value)

27 return Option(result)

28

29 def flatten(self) -> Option[Any]:

30 """Flatten Option[Option[T]] to Option[T]."""

31 if isinstance(self._value, Option):

32 return self._value # type: ignore[return-value]

33 return self # type: ignore[return-value]

34

35 def transpose(self) -> Any: # Returns Result[Option[T], E]

36 """Rust-style: Transpose Option[Result[T, E]] to Result[Option[T], E]."""

37 from ..result import Ok

38 # If the value is a Result, transpose it

39 if hasattr(self._value, 'is_ok') and callable(getattr(self._value, 'is_ok')):

40 if self._value.is_ok():

41 return Ok(Some(self._value.unwrap()))

42 else:

43 return self._value # Return the Err as-is

44 # If not a Result, wrap in Ok(Some(...))

45 return Ok(Some(self._value))

46

47 def zip(self, other: Option[U]) -> Option[tuple[T, U]]:

48 """Rust-style: Zip with another Option to create Option of tuple."""

49 if other.is_some():

50 from .some import Some

51 return Some((self._value, other.get()))

52 else:

53 return Nil()

54

55 def unzip(self) -> tuple[Option[Any], Option[Any]]:

56 """Unzip Option[tuple[A, B]] to tuple[Option[A], Option[B]]."""

57 if isinstance(self._value, tuple) and len(self._value) == 2:

58 from .some import Some

59 return Some(self._value[0]), Some(self._value[1])

60 else:

61 return Nil(), Nil()

62

63 def filter(self, predicate: Callable[[T], bool]) -> Option[T]:

64 if predicate(self._value):

65 return self

66 else:

67 return Nil()

68

69 # ========================================

70 # RUST API

71 # ========================================

72

73 def is_some(self) -> bool:

74 """Rust-style: Return True since this is Some."""

75 return True

76

77 def is_none(self) -> bool:

78 """Rust-style: Return False since this is Some."""

79 return False

80

81 def unwrap(self) -> T:

82 """Rust-style: Get the value."""

83 return self._value

84

85 def unwrap_or(self, default: T) -> T:

86 """Rust-style: Get the value, ignoring default."""

87 return self._value

88

89 def unwrap_or_else(self, func: Callable[[], T]) -> T:

90 """Rust-style: Get the value, not calling func."""

91 return self._value

92

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

94 """Rust-style: Get the value. For Some, message is ignored."""

95 return self._value

96

97 def and_then(self, func: Callable[[T], Option[U]]) -> Option[U]:

98 """Rust-style: Transform Some value to Option."""

99 return func(self._value)

100

101 def or_else_with(self, func: Callable[[], Option[T]]) -> Option[T]:

102 """Rust-style: Return self since Some doesn't need alternative."""

103 return self

104

105 def and_(self, other: Option[U]) -> Option[U]:

106 """Rust-style: Return other since self is Some."""

107 return other

108

109 def or_(self, other: Option[T]) -> Option[T]:

110 """Rust-style: Return self since self is Some."""

111 return self

112

113 def xor(self, other: Option[T]) -> Option[T]:

114 """Rust-style: Return Nil if other is Some, self if other is Nil."""

115 if other.is_some():

116 return Nil()

117 return self

118

119

120 def get_or_insert(self, value: T) -> T:

121 """Rust-style: Get the value (ignore insert since Some has value)."""

122 return self._value # type: ignore[no-any-return]

123

124 def get_or_insert_with(self, func: Callable[[], T]) -> T:

125 """Rust-style: Get the value (don't call func since Some has value)."""

126 return self._value # type: ignore[no-any-return]

127

128 def insert(self, value: T) -> T:

129 """Rust-style: Return the inserted value (for Some, return current value)."""

130 return self._value # type: ignore[no-any-return]

131

132 def map_or(self, default: U, func: Callable[[T], U]) -> U:

133 """Rust-style: Apply func to Some value."""

134 return func(self._value)

135

136 def map_or_else(self, default_func: Callable[[], U], func: Callable[[T], U]) -> U:

137 """Rust-style: Apply func to Some value (ignore default_func)."""

138 return func(self._value)

139

140 def ok_or(self, error: E) -> Any: # Returns Result[T, E]

141 """Rust-style: Convert Some(v) to Ok(v)."""

142 from ..result import Ok

143 return Ok(self._value)

144

145 def ok_or_else(self, func: Callable[[], E]) -> Any: # Returns Result[T, E]

146 """Rust-style: Convert Some(v) to Ok(v) (don't call func)."""

147 from ..result import Ok

148 return Ok(self._value)

149

150 def zip_with(self, other: Option[U], func: Callable[[T, U], V]) -> Option[V]:

151 """Rust-style: Zip with another Option and apply function."""

152 if other.is_some():

153 from .some import Some

154 return Some(func(self._value, other.get()))

155 else:

156 return Nil()

157

158 def inspect(self, func: Callable[[T], Any]) -> Option[T]:

159 """Rust-style: Call function with value and return self unchanged."""

160 func(self._value)

161 return self

162

163 # ========================================

164 # SCALA API

165 # ========================================

166

167 def get(self) -> T:

168 return self._value

169

170 def get_or_else(self, default: Union[T, Callable[[], T]]) -> T:

171 return self._value

172

173 def or_else(self, alternative: Union[Option[T], Callable[[], Option[T]]]) -> Option[T]:

174 return self

175

176 def filter_not(self, predicate: Callable[[T], bool]) -> Option[T]:

177 if not predicate(self._value):

178 return self

179 else:

180 return Nil()

181

182 def flat_map(self, func: Callable[[T], Option[U]]) -> Option[U]:

183 return func(self._value)

184

185 def fold(self, if_empty: U, func: Callable[[T], U]) -> U:

186 return func(self._value)

187

188 def foreach(self, func: Callable[[T], Any]) -> None:

189 """Scala-style: Apply func to value."""

190 func(self._value)

191

192 def exists(self, predicate: Callable[[T], bool]) -> bool:

193 return predicate(self._value)

194

195 def forall(self, predicate: Callable[[T], bool]) -> bool:

196 """Scala-style: True if value satisfies predicate."""

197 return predicate(self._value)

198

199 def contains(self, value: Any) -> bool:

200 """Scala-style: True if Option contains the specified value."""

201 return self._value == value # type: ignore[no-any-return]

202

203 def non_empty(self) -> bool:

204 """Scala-style: True since Some is not empty."""

205 return True

206

207 def or_null(self) -> Optional[T]:

208 """Scala-style: Return value since Some is defined."""

209 return self._value # type: ignore[no-any-return]

210

211 def or_none(self) -> Optional[T]:

212 """Return value since Some is defined. Alias for or_null."""

213 return self._value # type: ignore[no-any-return]

214

215 def to_list(self) -> list[T]:

216 return [self._value]

217

218 def to_optional(self) -> Optional[T]:

219 return self._value

220

221

222 # ========================================

223 # PYTHON SPECIAL METHODS

224 # ========================================

225

226 def __bool__(self) -> bool:

227 return True

228

229 def __iter__(self) -> Iterator[T]:

230 yield self._value

231

232 def __eq__(self, other: object) -> bool:

233 if isinstance(other, Some):

234 return self._value == other._value # type: ignore[no-any-return]

235 # Some is never equal to Nil or other types

236 return False

237

238 def __repr__(self) -> str:

239 return f"Some({self._value! r})"

240

241 def __str__(self) -> str:

242 return self.__repr__()