View Javadoc
1   package com.guinetik.corefun.examples;
2   
3   import com.guinetik.corefun.Result;
4   import com.guinetik.corefun.Try;
5   import java.io.IOException;
6   import java.nio.file.Path;
7   import java.nio.file.Paths;
8   import java.util.function.Supplier;
9   
10  /**
11   * Demonstrates usage of Try for converting exceptions to Results.
12   *
13   * Try is a utility class that bridges exception-throwing code with
14   * the functional Result type. It captures exceptions and converts
15   * them to Failure results, enabling seamless integration of legacy
16   * exception-based APIs with functional error handling.
17   */
18  public class TryExample {
19  
20      public static void main(String[] args) {
21          System.out.println("=== Try Utility Examples ===\n");
22  
23          basicTry();
24          tryWithExceptionDetails();
25          tryWithCustomErrorMapping();
26          tryRunnables();
27          lazyEvaluation();
28          getOrDefaultPatterns();
29      }
30  
31      /**
32       * Basic Try.of() - converts exceptions to string error messages.
33       */
34      static void basicTry() {
35          System.out.println("--- Basic Try.of() ---");
36  
37          // Successful operation
38          Result<Integer, String> success = Try.of(() -> Integer.parseInt("42"));
39          System.out.println("Parse '42': " + success);
40  
41          // Failed operation - exception becomes error message
42          Result<Integer, String> failure = Try.of(() ->
43              Integer.parseInt("not-a-number")
44          );
45          System.out.println("Parse 'not-a-number': " + failure);
46  
47          // Chaining with Result operations
48          Result<String, String> doubled = Try.of(() -> Integer.parseInt("21"))
49              .map(n -> n * 2)
50              .map(n -> "Result: " + n);
51          System.out.println("Parsed and doubled: " + doubled);
52  
53          System.out.println();
54      }
55  
56      /**
57       * Try.ofException() - preserves the full exception.
58       */
59      static void tryWithExceptionDetails() {
60          System.out.println("--- Try.ofException() ---");
61  
62          // When you need the actual exception (for logging, stack traces, etc.)
63          Result<Integer, Exception> result = Try.ofException(() ->
64              Integer.parseInt("bad")
65          );
66  
67          result.peekFailure(ex -> {
68              System.out.println(
69                  "Exception type: " + ex.getClass().getSimpleName()
70              );
71              System.out.println("Exception message: " + ex.getMessage());
72          });
73  
74          // You can map the exception to your own error type
75          Result<Integer, String> mapped = result.mapFailure(
76              ex -> ex.getClass().getSimpleName() + ": " + ex.getMessage()
77          );
78          System.out.println("Mapped error: " + mapped);
79  
80          System.out.println();
81      }
82  
83      /**
84       * Try.of() with custom error mapping.
85       */
86      static void tryWithCustomErrorMapping() {
87          System.out.println("--- Custom Error Mapping ---");
88  
89          // Map exceptions to your own error type
90          Result<Path, FileError> fileResult = Try.of(
91              () -> Paths.get("/nonexistent/file.txt").toRealPath(),
92              ex -> new FileError(ex.getMessage(), "FILE_NOT_FOUND")
93          );
94          System.out.println("File operation: " + fileResult);
95  
96          fileResult.peekFailure(error ->
97              System.out.println("  Error code: " + error.code)
98          );
99  
100         // Useful for creating domain-specific errors
101         Result<Integer, ApiError> apiResult = Try.of(
102             () -> callExternalApi(),
103             ex -> new ApiError(500, "Service unavailable: " + ex.getMessage())
104         );
105         System.out.println("API call: " + apiResult);
106 
107         System.out.println();
108     }
109 
110     /**
111      * Try.run() - for void operations.
112      */
113     static void tryRunnables() {
114         System.out.println("--- Try.run() for Void Operations ---");
115 
116         // Successful void operation
117         Result<Void, String> success = Try.run(() -> {
118             System.out.println("  Executing some work...");
119             // operation succeeds
120         });
121         System.out.println("Run success: " + success);
122 
123         // Failed void operation
124         Result<Void, String> failure = Try.run(() -> {
125             throw new RuntimeException("Simulated failure");
126         });
127         System.out.println("Run failure: " + failure);
128 
129         // Check success/failure
130         if (success.isSuccess()) {
131             System.out.println("  Operation completed successfully");
132         }
133 
134         System.out.println();
135     }
136 
137     /**
138      * Try.lazy() - deferred execution.
139      */
140     static void lazyEvaluation() {
141         System.out.println("--- Lazy Evaluation ---");
142 
143         // Create a lazy computation (not executed yet)
144         Supplier<Result<String, String>> lazyRead = Try.lazy(() -> {
145             System.out.println("  [Executing lazy operation now]");
146             return readConfigValue("app.name");
147         });
148 
149         System.out.println("Lazy supplier created (not executed yet)");
150         System.out.println("Calling get()...");
151 
152         // Now it executes
153         Result<String, String> result = lazyRead.get();
154         System.out.println("Result: " + result);
155 
156         // Useful for conditional execution
157         boolean shouldRun = true;
158         if (shouldRun) {
159             Supplier<Result<Integer, String>> expensive = Try.lazy(() -> {
160                 Thread.sleep(10); // Simulate expensive operation
161                 return 42;
162             });
163             System.out.println("Expensive computation: " + expensive.get());
164         }
165 
166         System.out.println();
167     }
168 
169     /**
170      * getOrDefault and getOrNull patterns.
171      */
172     static void getOrDefaultPatterns() {
173         System.out.println("--- getOrDefault / getOrNull ---");
174 
175         // When you just need a value with a fallback (no Result wrapping)
176         int parsed = Try.getOrDefault(() -> Integer.parseInt("bad"), 0);
177         System.out.println("getOrDefault with bad input: " + parsed);
178 
179         int goodParsed = Try.getOrDefault(() -> Integer.parseInt("42"), 0);
180         System.out.println("getOrDefault with good input: " + goodParsed);
181 
182         // getOrNull for nullable scenarios
183         String nullResult = Try.getOrNull(() -> {
184             throw new RuntimeException("Error");
185         });
186         System.out.println("getOrNull on error: " + nullResult);
187 
188         String validResult = Try.getOrNull(() -> "Hello");
189         System.out.println("getOrNull on success: " + validResult);
190 
191         // Practical example: configuration with fallback
192         String dbHost = Try.getOrDefault(
193             () -> System.getenv("DB_HOST"),
194             "localhost"
195         );
196         System.out.println(
197             "DB_HOST (with fallback): " +
198                 (dbHost != null ? dbHost : "localhost")
199         );
200 
201         System.out.println();
202     }
203 
204     // --- Helper methods and classes ---
205 
206     static String readConfigValue(String key) throws IOException {
207         // Simulated config reading
208         if ("app.name".equals(key)) {
209             return "MyApp";
210         }
211         throw new IOException("Config key not found: " + key);
212     }
213 
214     static Integer callExternalApi() throws Exception {
215         throw new Exception("Connection refused");
216     }
217 
218     static class FileError {
219 
220         final String message;
221         final String code;
222 
223         FileError(String message, String code) {
224             this.message = message;
225             this.code = code;
226         }
227 
228         @Override
229         public String toString() {
230             return "FileError{" + code + ": " + message + "}";
231         }
232     }
233 
234     static class ApiError {
235 
236         final int statusCode;
237         final String message;
238 
239         ApiError(int statusCode, String message) {
240             this.statusCode = statusCode;
241             this.message = message;
242         }
243 
244         @Override
245         public String toString() {
246             return "ApiError{" + statusCode + ": " + message + "}";
247         }
248     }
249 }