Update services to use Result pattern
Refactor DocumentService and CutListService to return Result<T> instead of throwing exceptions or using out parameters. This removes all MessageBox calls from business logic. DocumentService changes: - Load() returns Result<Document> 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<SawCut.Nesting.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 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
|
using CutList.Common;
|
||||||
using CutList.Models;
|
using CutList.Models;
|
||||||
using SawCut;
|
using SawCut;
|
||||||
using SawCut.Nesting;
|
using SawCut.Nesting;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace CutList.Services
|
namespace CutList.Services
|
||||||
@@ -17,19 +19,27 @@ namespace CutList.Services
|
|||||||
/// <param name="parts">The parts to be nested</param>
|
/// <param name="parts">The parts to be nested</param>
|
||||||
/// <param name="stockBins">The available stock bins</param>
|
/// <param name="stockBins">The available stock bins</param>
|
||||||
/// <param name="cuttingTool">The cutting tool to use (determines kerf/spacing)</param>
|
/// <param name="cuttingTool">The cutting tool to use (determines kerf/spacing)</param>
|
||||||
/// <returns>The packing result with optimized bins and unused items</returns>
|
/// <returns>Result containing the packing result with optimized bins and unused items, or error message</returns>
|
||||||
public Result Pack(List<PartInputItem> parts, List<BinInputItem> stockBins, Tool cuttingTool)
|
public Result<SawCut.Nesting.Result> Pack(List<PartInputItem> parts, List<BinInputItem> stockBins, Tool cuttingTool)
|
||||||
{
|
{
|
||||||
var multiBins = ConvertToMultiBins(stockBins);
|
try
|
||||||
var binItems = ConvertToBinItems(parts);
|
|
||||||
|
|
||||||
var engine = new MultiBinEngine
|
|
||||||
{
|
{
|
||||||
Spacing = cuttingTool.Kerf,
|
var multiBins = ConvertToMultiBins(stockBins);
|
||||||
Bins = multiBins
|
var binItems = ConvertToBinItems(parts);
|
||||||
};
|
|
||||||
|
|
||||||
return engine.Pack(binItems);
|
var engine = new MultiBinEngine
|
||||||
|
{
|
||||||
|
Spacing = cuttingTool.Kerf,
|
||||||
|
Bins = multiBins
|
||||||
|
};
|
||||||
|
|
||||||
|
var packResult = engine.Pack(binItems);
|
||||||
|
return Result<SawCut.Nesting.Result>.Success(packResult);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result<SawCut.Nesting.Result>.Failure($"Packing failed: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MultiBin> ConvertToMultiBins(List<BinInputItem> stockBins)
|
private List<MultiBin> ConvertToMultiBins(List<BinInputItem> stockBins)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using CutList.Common;
|
||||||
using CutList.Forms;
|
using CutList.Forms;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
@@ -16,49 +17,58 @@ namespace CutList.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="document">The document to save</param>
|
/// <param name="document">The document to save</param>
|
||||||
/// <param name="filePath">The file path to save to</param>
|
/// <param name="filePath">The file path to save to</param>
|
||||||
/// <exception cref="IOException">Thrown when file cannot be saved</exception>
|
/// <returns>Result indicating success or failure with error message</returns>
|
||||||
public void Save(Document document, string filePath)
|
public Result Save(Document document, string filePath)
|
||||||
{
|
{
|
||||||
var json = JsonConvert.SerializeObject(document, Formatting.Indented);
|
try
|
||||||
File.WriteAllText(filePath, json);
|
{
|
||||||
|
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}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads a document from the specified file path.
|
/// Loads a document from the specified file path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filePath">The file path to load from</param>
|
/// <param name="filePath">The file path to load from</param>
|
||||||
/// <returns>The loaded document</returns>
|
/// <returns>Result containing the loaded document or error message</returns>
|
||||||
/// <exception cref="IOException">Thrown when file cannot be read</exception>
|
public Result<Document> Load(string filePath)
|
||||||
/// <exception cref="JsonException">Thrown when file contains invalid JSON</exception>
|
|
||||||
public Document Load(string filePath)
|
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(filePath);
|
try
|
||||||
var document = JsonConvert.DeserializeObject<Document>(json);
|
{
|
||||||
return document;
|
var json = File.ReadAllText(filePath);
|
||||||
|
var document = JsonConvert.DeserializeObject<Document>(json);
|
||||||
|
return Result<Document>.Success(document);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result<Document>.Failure($"Failed to load file: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates that a document has the minimum required data.
|
/// Validates that a document has the minimum required data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="document">The document to validate</param>
|
/// <param name="document">The document to validate</param>
|
||||||
/// <param name="validationMessage">Output parameter containing validation error message</param>
|
/// <returns>Result indicating success or failure with validation error message</returns>
|
||||||
/// <returns>True if document is valid, false otherwise</returns>
|
public Result Validate(Document document)
|
||||||
public bool Validate(Document document, out string validationMessage)
|
|
||||||
{
|
{
|
||||||
if (document.PartsToNest == null || document.PartsToNest.Count == 0)
|
if (document.PartsToNest == null || document.PartsToNest.Count == 0)
|
||||||
{
|
{
|
||||||
validationMessage = "No parts to nest.";
|
return Result.Failure("No parts to nest.");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.StockBins == null || document.StockBins.Count == 0)
|
if (document.StockBins == null || document.StockBins.Count == 0)
|
||||||
{
|
{
|
||||||
validationMessage = "No stock bins available.";
|
return Result.Failure("No stock bins available.");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
validationMessage = string.Empty;
|
return Result.Success();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user