From c8fd22f5b14a1e3f632d766694d938c06707f0cb Mon Sep 17 00:00:00 2001 From: AJ Date: Tue, 18 Nov 2025 17:43:33 -0500 Subject: [PATCH] Update services to use Result pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor DocumentService and CutListService to return Result instead of throwing exceptions or using out parameters. This removes all MessageBox calls from business logic. DocumentService changes: - Load() returns Result instead of Document - Save() returns Result instead of void - Validate() returns Result instead of bool with out parameter - All exceptions caught and converted to Result.Failure CutListService changes: - Pack() returns Result - Exceptions caught and converted to Result.Failure Benefits: - Services are now UI-agnostic (no MessageBox) - Can be unit tested without UI dependencies - Can be reused in console apps, web apps, etc. - Errors are values, not exceptions - Clear, type-safe error handling contract 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CutList/Services/CutListService.cs | 30 +++++++++++------ CutList/Services/DocumentService.cs | 50 +++++++++++++++++------------ 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/CutList/Services/CutListService.cs b/CutList/Services/CutListService.cs index dde5d1e..3b0817c 100644 --- a/CutList/Services/CutListService.cs +++ b/CutList/Services/CutListService.cs @@ -1,6 +1,8 @@ +using CutList.Common; using CutList.Models; using SawCut; using SawCut.Nesting; +using System; using System.Collections.Generic; namespace CutList.Services @@ -17,19 +19,27 @@ namespace CutList.Services /// The parts to be nested /// The available stock bins /// The cutting tool to use (determines kerf/spacing) - /// The packing result with optimized bins and unused items - public Result Pack(List parts, List stockBins, Tool cuttingTool) + /// Result containing the packing result with optimized bins and unused items, or error message + public Result Pack(List parts, List stockBins, Tool cuttingTool) { - var multiBins = ConvertToMultiBins(stockBins); - var binItems = ConvertToBinItems(parts); - - var engine = new MultiBinEngine + try { - Spacing = cuttingTool.Kerf, - Bins = multiBins - }; + var multiBins = ConvertToMultiBins(stockBins); + var binItems = ConvertToBinItems(parts); - return engine.Pack(binItems); + var engine = new MultiBinEngine + { + Spacing = cuttingTool.Kerf, + Bins = multiBins + }; + + var packResult = engine.Pack(binItems); + return Result.Success(packResult); + } + catch (Exception ex) + { + return Result.Failure($"Packing failed: {ex.Message}"); + } } private List ConvertToMultiBins(List stockBins) diff --git a/CutList/Services/DocumentService.cs b/CutList/Services/DocumentService.cs index 460cae5..87d4d58 100644 --- a/CutList/Services/DocumentService.cs +++ b/CutList/Services/DocumentService.cs @@ -1,3 +1,4 @@ +using CutList.Common; using CutList.Forms; using Newtonsoft.Json; using System; @@ -16,49 +17,58 @@ namespace CutList.Services /// /// The document to save /// The file path to save to - /// Thrown when file cannot be saved - public void Save(Document document, string filePath) + /// Result indicating success or failure with error message + public Result Save(Document document, string filePath) { - var json = JsonConvert.SerializeObject(document, Formatting.Indented); - File.WriteAllText(filePath, json); + try + { + var json = JsonConvert.SerializeObject(document, Formatting.Indented); + File.WriteAllText(filePath, json); + return Result.Success(); + } + catch (Exception ex) + { + return Result.Failure($"Failed to save file: {ex.Message}"); + } } /// /// Loads a document from the specified file path. /// /// The file path to load from - /// The loaded document - /// Thrown when file cannot be read - /// Thrown when file contains invalid JSON - public Document Load(string filePath) + /// Result containing the loaded document or error message + public Result Load(string filePath) { - var json = File.ReadAllText(filePath); - var document = JsonConvert.DeserializeObject(json); - return document; + try + { + var json = File.ReadAllText(filePath); + var document = JsonConvert.DeserializeObject(json); + return Result.Success(document); + } + catch (Exception ex) + { + return Result.Failure($"Failed to load file: {ex.Message}"); + } } /// /// Validates that a document has the minimum required data. /// /// The document to validate - /// Output parameter containing validation error message - /// True if document is valid, false otherwise - public bool Validate(Document document, out string validationMessage) + /// Result indicating success or failure with validation error message + public Result Validate(Document document) { if (document.PartsToNest == null || document.PartsToNest.Count == 0) { - validationMessage = "No parts to nest."; - return false; + return Result.Failure("No parts to nest."); } if (document.StockBins == null || document.StockBins.Count == 0) { - validationMessage = "No stock bins available."; - return false; + return Result.Failure("No stock bins available."); } - validationMessage = string.Empty; - return true; + return Result.Success(); } } }