OperationResult<T> Class

A result wrapper that encapsulates success/failure state and payload for all API operations.

namespace StoryCADLib.Services.API

public class OperationResult<T>
# (no direct Python equivalent: the Python binding has no OperationResult wrapper.
#  Methods on the StoryCAD object return their payload directly and raise on error.)
from storycad import StoryCAD

Overview

All StoryCADLib API methods return OperationResult<T> instead of throwing exceptions. This pattern ensures:

  • Safe consumption - No unexpected exceptions for external callers
  • Explicit error handling - Errors are communicated through properties
  • Consistent interface - All methods follow the same result pattern

Properties

Property Type Description
IsSuccess bool True if the operation succeeded
Payload T The result data (only valid if IsSuccess is true)
ErrorMessage string Description of what went wrong (only set if IsSuccess is false)

Usage Pattern

Always check IsSuccess before accessing Payload:

var result = await api.CreateEmptyOutline("My Story", "Author", "0");

if (result.IsSuccess)
{
    // Safe to use Payload
    var guids = result.Payload;
    Console.WriteLine($"Created {guids.Count} elements");
}
else
{
    // Handle error
    Console.WriteLine($"Error: {result.ErrorMessage}");
}
# The Python wrapper raises on error instead of returning a result object.
try:
    elements = sc.create_empty_outline("My Story", "Author", template_index="0")
    print(f"Created {len(elements)} elements")
except Exception as error:
    print(f"Error: {error}")

Static Factory Methods

Success

Creates a successful result with a payload.

public static OperationResult<T> Success(T payload)
# (no direct Python equivalent: there is no OperationResult to construct.
#  A successful Python call simply returns the payload directly.)

Example:

return OperationResult<List<Guid>>.Success(elementGuids);
# (no direct Python equivalent: Python returns the payload directly instead of
#  wrapping it in a success result.)
return element_guids

Failure

Creates a failed result with an error message.

public static OperationResult<T> Failure(string errorMessage)
# (no direct Python equivalent: there is no failure result to construct.
#  The Python wrapper signals failure by raising an exception.)

Example:

return OperationResult<bool>.Failure("Element not found");
# (no direct Python equivalent: Python raises instead of returning a failure result.)
raise Exception("Element not found")

SafeExecuteAsync

Wraps an async operation, catching exceptions and converting them to failure results.

public static async Task<OperationResult<T>> SafeExecuteAsync(Task<T> operation)
# (no direct Python equivalent: there is no SafeExecuteAsync wrapper.
#  Python lets exceptions propagate; callers wrap calls in try/except as needed.)

Example:

var result = await OperationResult<StoryModel>.SafeExecuteAsync(
    outlineService.CreateModel(name, author, templateIndex));
# (no direct Python equivalent: Python has no SafeExecuteAsync wrapper.
#  Call the method directly and handle errors with try/except.)
try:
    elements = sc.create_empty_outline(name, author, template_index=template_index)
except Exception as error:
    print(f"Error: {error}")

Best Practices

Do: Check IsSuccess First

// Good
var result = api.GetStoryElement(guid);
if (result.IsSuccess)
{
    var element = result.Payload;
    // Use element...
}
# Good - the Python wrapper raises on error, so wrap the call in try/except.
try:
    element = sc.get_element(handle)
    # Use element...
except Exception as error:
    print(f"Error: {error}")

Don’t: Access Payload Without Checking

// Bad - Payload may be null/default if operation failed
var element = api.GetStoryElement(guid).Payload;
# Bad - an unhandled error here raises and crashes the caller.
element = sc.get_element(handle)

Do: Propagate Errors

public OperationResult<bool> MyOperation()
{
    var result = api.GetStoryElement(guid);
    if (!result.IsSuccess)
    {
        return OperationResult<bool>.Failure(result.ErrorMessage);
    }

    // Continue with operation...
    return OperationResult<bool>.Success(true);
}
# Python propagates errors by letting the exception bubble up: no result wrapper.
def my_operation():
    element = sc.get_element(handle)  # raises if the element is not found

    # Continue with operation...
    return True

Do: Provide Meaningful Error Messages

Error messages should help diagnose the problem:

// Good
return OperationResult<T>.Failure(
    $"Element with GUID {guid} not found in current model");

// Not helpful
return OperationResult<T>.Failure("Error");
# Python raises instead of returning a failure result; give the exception a clear message.
# Good
raise Exception(f"Element with GUID {guid} not found in current model")

# Not helpful
raise Exception("Error")

Common Return Types

Method Returns
CreateEmptyOutline OperationResult<List<Guid>>
OpenOutline OperationResult<bool>
WriteOutline OperationResult<string>
GetAllElements OperationResult<ObservableCollection<StoryElement>>
GetStoryElement OperationResult<StoryElement>
AddElement OperationResult<Guid>
UpdateElementProperty OperationResult<StoryElement>
DeleteElement OperationResult<bool>
SearchForText OperationResult<List<Dictionary<string, object>>>