Skip to content

Commit d51e6d1

Browse files
docs: Add comprehensive documentation and usage examples to Result<T>
Add detailed documentation to the Result<T> functional interface: Class-level documentation: - Explains Rust-style error handling philosophy - Lists key benefits (type-safe, composable, zero-cost, explicit) - Provides 5 comprehensive usage examples: * Basic usage with is_ok()/value()/error() * Using value_or() for safe defaults * Pattern matching with match() * Chaining operations functionally * Creating Results with Ok()/Err() Method-level documentation: - is_ok(), is_error(): Boolean checks - value(): Extract success value (with safety notes) - error(): Extract error value - value_or(): Safe extraction with fallback - match(): Pattern matching with detailed examples Factory function documentation: - Ok(): Creating success Results (with/without values) - Err(): Creating error Results (with type specification notes) - VoidResult: Explained use case for void operations Examples show real-world usage patterns including: - Parse validation with error handling - Logging both success and error cases - Transforming Results with callbacks - Safe extraction without exceptions - Chaining operations functionally All documentation uses @Par example blocks for clarity. Tests continue passing (103/103).
1 parent b288649 commit d51e6d1

1 file changed

Lines changed: 206 additions & 1 deletion

File tree

include/lx200/lx200.hpp

Lines changed: 206 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,42 +114,185 @@ struct Unit {};
114114
/**
115115
* @brief Functional Result<T> type based on std::variant
116116
*
117-
* Type-safe error handling that replaces error codes.
117+
* Provides Rust-style error handling without exceptions or error codes.
118+
* A Result<T> either contains a successful value (Ok) or an error (Err).
119+
*
120+
* Key Benefits:
121+
* - Type-safe: Cannot accidentally ignore errors
122+
* - Composable: Chain operations with match()
123+
* - Zero-cost: No exceptions or dynamic allocation
124+
* - Explicit: Error handling is visible in function signatures
125+
*
126+
* @par Basic Usage:
127+
* @code
128+
* // Function returns Result instead of throwing or returning error codes
129+
* Result<RACoordinate> coord = parse_ra("12:34:56", PrecisionMode::High);
130+
*
131+
* // Check if successful
132+
* if (coord.is_ok()) {
133+
* RACoordinate ra = coord.value();
134+
* LOG_INF("RA: %02d:%02d:%02d", ra.hours, ra.minutes, ra.seconds);
135+
* } else {
136+
* ParseError err = coord.error();
137+
* LOG_ERR("Parse failed: %d", static_cast<int>(err));
138+
* }
139+
* @endcode
140+
*
141+
* @par Using value_or() for Defaults:
142+
* @code
143+
* // Provide fallback value on error
144+
* RACoordinate ra = parse_ra(input, mode).value_or(RACoordinate{0, 0, 0});
145+
* @endcode
146+
*
147+
* @par Pattern Matching with match():
148+
* @code
149+
* // Handle both cases functionally
150+
* auto message = parse_ra(input, mode).match(
151+
* [](const RACoordinate& ra) {
152+
* return format_ra(ra); // Success path
153+
* },
154+
* [](ParseError err) {
155+
* return std::string("Invalid"); // Error path
156+
* }
157+
* );
158+
* @endcode
159+
*
160+
* @par Chaining Operations:
161+
* @code
162+
* // Parse and validate in one expression
163+
* auto result = parse_ra(input, mode).match(
164+
* [](const RACoordinate& ra) -> Result<std::string> {
165+
* if (RACoordinate::is_valid(ra.hours, ra.minutes, ra.seconds)) {
166+
* return Ok(format_ra(ra));
167+
* }
168+
* return Err<std::string>(ParseError::OutOfRange);
169+
* },
170+
* [](ParseError err) -> Result<std::string> {
171+
* return Err<std::string>(err);
172+
* }
173+
* );
174+
* @endcode
175+
*
176+
* @par Creating Results:
177+
* @code
178+
* // Use factory functions for clarity
179+
* Result<int> success = Ok(42);
180+
* Result<int> failure = Err<int>(ParseError::InvalidFormat);
181+
* VoidResult void_ok = Ok(); // For void operations
182+
* VoidResult void_err = Err<Unit>(ParseError::General);
183+
* @endcode
184+
*
185+
* @tparam T The type of the success value
186+
*
187+
* @see Ok() - Factory for successful Results
188+
* @see Err() - Factory for error Results
189+
* @see VoidResult - Typedef for Result<Unit> (void operations)
118190
*/
119191
template<typename T>
120192
class Result {
121193
private:
122194
std::variant<T, ParseError> data_;
123195

124196
public:
197+
/// Construct Result with success value (copy)
125198
constexpr explicit Result(const T& value) noexcept : data_(value) {}
199+
200+
/// Construct Result with success value (move)
126201
constexpr explicit Result(T&& value) noexcept : data_(std::move(value)) {}
202+
203+
/// Construct Result with error
127204
constexpr explicit Result(ParseError error) noexcept : data_(error) {}
128205

206+
/**
207+
* @brief Check if Result contains a success value
208+
* @return true if Ok, false if Err
209+
*/
129210
constexpr bool is_ok() const noexcept {
130211
return std::holds_alternative<T>(data_);
131212
}
132213

214+
/**
215+
* @brief Check if Result contains an error
216+
* @return true if Err, false if Ok
217+
*/
133218
constexpr bool is_error() const noexcept {
134219
return std::holds_alternative<ParseError>(data_);
135220
}
136221

222+
/**
223+
* @brief Extract the success value (const)
224+
* @return Reference to the contained value
225+
* @throws std::bad_variant_access if Result is an error
226+
* @note Always check is_ok() before calling, or use value_or() for safety
227+
*/
137228
constexpr const T& value() const {
138229
return std::get<T>(data_);
139230
}
140231

232+
/**
233+
* @brief Extract the success value (mutable)
234+
* @return Mutable reference to the contained value
235+
* @throws std::bad_variant_access if Result is an error
236+
* @note Always check is_ok() before calling, or use value_or() for safety
237+
*/
141238
constexpr T& value() {
142239
return std::get<T>(data_);
143240
}
144241

242+
/**
243+
* @brief Extract the error value
244+
* @return The contained ParseError
245+
* @throws std::bad_variant_access if Result is Ok
246+
* @note Always check is_error() before calling
247+
*/
145248
constexpr ParseError error() const {
146249
return std::get<ParseError>(data_);
147250
}
148251

252+
/**
253+
* @brief Extract value or return default on error
254+
* @param default_value Value to return if Result is an error
255+
* @return The contained value if Ok, or default_value if Err
256+
*
257+
* @par Example:
258+
* @code
259+
* // Safe extraction without checking
260+
* RACoordinate ra = parse_ra(input, mode).value_or(RACoordinate{0, 0, 0});
261+
* @endcode
262+
*/
149263
constexpr T value_or(const T& default_value) const {
150264
return is_ok() ? value() : default_value;
151265
}
152266

267+
/**
268+
* @brief Pattern match on Result with callbacks
269+
* @param ok_fn Callback for success case: T -> R
270+
* @param err_fn Callback for error case: ParseError -> R
271+
* @return Result of calling the appropriate callback
272+
*
273+
* This is the functional way to handle Results, similar to Rust's match.
274+
* Both callbacks must return the same type R.
275+
*
276+
* @par Example - Simple Logging:
277+
* @code
278+
* parse_ra(input, mode).match(
279+
* [](const RACoordinate& ra) {
280+
* LOG_INF("Parsed: %02d:%02d:%02d", ra.hours, ra.minutes, ra.seconds);
281+
* },
282+
* [](ParseError err) {
283+
* LOG_ERR("Parse error: %d", static_cast<int>(err));
284+
* }
285+
* );
286+
* @endcode
287+
*
288+
* @par Example - Transforming Results:
289+
* @code
290+
* std::string message = parse_ra(input, mode).match(
291+
* [](const RACoordinate& ra) { return format_ra(ra); },
292+
* [](ParseError err) { return std::string("ERROR"); }
293+
* );
294+
* @endcode
295+
*/
153296
template<typename OkFn, typename ErrFn>
154297
constexpr auto match(OkFn&& ok_fn, ErrFn&& err_fn) const {
155298
return is_ok() ? ok_fn(value()) : err_fn(error());
@@ -158,28 +301,90 @@ class Result {
158301

159302
/**
160303
* @brief Result type for void operations
304+
*
305+
* Use this for functions that can fail but don't return a value.
306+
* Equivalent to Result<Unit>.
307+
*
308+
* @par Example:
309+
* @code
310+
* VoidResult validate_input(const char* input) {
311+
* if (input == nullptr) {
312+
* return Err<Unit>(ParseError::InvalidFormat);
313+
* }
314+
* return Ok(); // Success with no value
315+
* }
316+
* @endcode
161317
*/
162318
using VoidResult = Result<Unit>;
163319

164320
/**
165321
* @brief Factory for successful Results
322+
*
323+
* Creates a Result<T> containing a success value. Use this instead of
324+
* calling Result<T> constructor directly for better type inference.
325+
*
326+
* @param value The success value to wrap
327+
* @return Result<T> containing the value
328+
*
329+
* @par Example - With Value:
330+
* @code
331+
* Result<int> parse_number(const char* str) {
332+
* int val = atoi(str);
333+
* if (val == 0 && str[0] != '0') {
334+
* return Err<int>(ParseError::InvalidFormat);
335+
* }
336+
* return Ok(val); // Type deduced as Result<int>
337+
* }
338+
* @endcode
339+
*
340+
* @par Example - Void (No Value):
341+
* @code
342+
* VoidResult check_bounds(int value) {
343+
* if (value < 0 || value > 100) {
344+
* return Err<Unit>(ParseError::OutOfRange);
345+
* }
346+
* return Ok(); // Success with no value
347+
* }
348+
* @endcode
166349
*/
167350
template<typename T>
168351
constexpr Result<std::remove_reference_t<T>> Ok(const T& value) noexcept {
169352
return Result<std::remove_reference_t<T>>(value);
170353
}
171354

355+
/// Factory for successful Results (move version)
172356
template<typename T>
173357
constexpr Result<std::remove_reference_t<T>> Ok(T&& value) noexcept {
174358
return Result<std::remove_reference_t<T>>(std::forward<T>(value));
175359
}
176360

361+
/// Factory for successful void Results
177362
constexpr VoidResult Ok() noexcept {
178363
return VoidResult(Unit{});
179364
}
180365

181366
/**
182367
* @brief Factory for error Results
368+
*
369+
* Creates a Result<T> containing an error. The type T must be explicitly
370+
* specified since it cannot be inferred from the error value.
371+
*
372+
* @tparam T The success type (even though we're creating an error)
373+
* @param error The error to wrap
374+
* @return Result<T> containing the error
375+
*
376+
* @par Example:
377+
* @code
378+
* Result<RACoordinate> parse_ra(const char* str) {
379+
* if (str == nullptr) {
380+
* return Err<RACoordinate>(ParseError::InvalidFormat);
381+
* }
382+
* // ... parsing logic ...
383+
* return Ok(coordinate);
384+
* }
385+
* @endcode
386+
*
387+
* @note Type must be specified: Err<int>(error), not Err(error)
183388
*/
184389
template<typename T>
185390
constexpr Result<std::remove_reference_t<T>> Err(ParseError error) noexcept {

0 commit comments

Comments
 (0)