sharplib/imm/FSM.cs
Marc Hernandez 3f850cc9b0 Implement Xml Serialization Framework with Type Handling and Metadata Caching
- Introduced XmlSer class for XML serialization and deserialization.
- Added TypeMetaCache for caching type metadata and reflection information.
- Implemented various ITypeHandler implementations for handling different types (Primitive, Proxy, ISerializable, Collection, Object).
- Enhanced type resolution with TypeResolver to manage type lookups and conversions.
- Established a configuration class (XmlCfg) to manage serialization settings.
- Integrated support for handling graphs and references in serialized objects.
- Added extensive member processing and filtering based on attributes.
- Ensured compatibility with immutable collections and various data structures.
- Implemented detailed error handling and logging for serialization processes.
2025-05-28 10:46:00 -07:00

110 lines
3.9 KiB
C#

#nullable enable
using System;
using System.Runtime.CompilerServices;
using imm; // Ensure this namespace is available
/// <summary>
/// Base context for an FSM.
/// MUST inherit from Recorded<TSelf> or Timed<TSelf> in your concrete class.
/// </summary>
/// <typeparam name="TSelf">The concrete Context type.</typeparam>
public abstract record class FsmContextBase<TSelf> : Recorded<TSelf>
where TSelf : FsmContextBase<TSelf>
{
// Required for 'with' expressions.
protected FsmContextBase(Recorded<TSelf> original) : base(original) { }
protected FsmContextBase() { }
}
/// <summary>
/// Base state for an FSM.
/// MUST inherit from Recorded<TSelf> or Timed<TSelf> in your concrete class.
/// </summary>
/// <typeparam name="TSelf">The concrete State type.</typeparam>
/// <typeparam name="TCtx">The concrete Context type (must be based on FsmContextBase).</typeparam>
public abstract record class FsmStateBase<TSelf, TCtx> : Recorded<TSelf>
where TSelf : FsmStateBase<TSelf, TCtx>
where TCtx : FsmContextBase<TCtx>
{
/// <summary>
/// Called when entering this state.
/// </summary>
public virtual (TCtx Context, TSelf State) OnEnter(TCtx context, FsmStateBase<TSelf, TCtx> oldState)
{
return (context, (TSelf)this);
}
/// <summary>
/// Called when exiting this state.
/// </summary>
public virtual (TCtx Context, TSelf State) OnExit(TCtx context, FsmStateBase<TSelf, TCtx> newState)
{
return (context, (TSelf)this);
}
// Required for 'with' expressions.
protected FsmStateBase(Recorded<TSelf> original) : base(original) { }
protected FsmStateBase() { }
}
/// <summary>
/// An immutable FSM base class.
/// MUST inherit from Recorded<TSelf> or Timed<TSelf> in your concrete class.
/// </summary>
/// <typeparam name="TSelf">The concrete FSM type.</typeparam>
/// <typeparam name="TState">The concrete State type.</typeparam>
/// <typeparam name="TCtx">The concrete Context type.</typeparam>
public abstract record class FsmBase<TSelf, TState, TCtx> : Recorded<TSelf>
where TSelf : FsmBase<TSelf, TState, TCtx>
where TState : FsmStateBase<TState, TCtx>
where TCtx : FsmContextBase<TCtx>
{
public TCtx Context { get; init; }
public TState State { get; init; }
protected FsmBase(TCtx initialContext, TState initialState)
{
Context = initialContext;
State = initialState;
}
// Required for 'with' expressions.
protected FsmBase(Recorded<TSelf> original) : base(original)
{
var o = original as FsmBase<TSelf, TState, TCtx>;
Context = o!.Context;
State = o!.State;
}
/// <summary>
/// Transitions the FSM. It automatically uses the 'Process'
/// method appropriate for Recorded or Timed, thanks to virtual overrides.
/// </summary>
public TSelf Transition(
TState newState,
string reason,
[CallerMemberName] string memberName = "",
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0,
[CallerArgumentExpression("newState")] string expression = "")
{
Console.WriteLine($"[FSM] Transition: {State.GetType().Name} -> {newState.GetType().Name}. Reason: {reason}");
var (ctxAfterExit, stateAfterExit) = State.OnExit(Context, newState);
var (ctxAfterEnter, stateAfterEnter) = newState.OnEnter(ctxAfterExit, stateAfterExit);
// Since 'this' is at least 'Recorded<TSelf>', we can call the
// detailed 'Process'. If 'this' is actually 'Timed<TSelf>', C#'s
// virtual dispatch will call the 'Timed' override automatically.
return Process(
fsm => (TSelf)fsm with
{
Context = ctxAfterEnter,
State = stateAfterEnter
},
$"Transition to {newState.GetType().Name}: {reason}",
memberName, filePath, lineNumber, expression
);
}
}