Update the immutibility names. Add net.10.0
This commit is contained in:
parent
c25b9b3b12
commit
65e7ed1b24
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net9</TargetFrameworks>
|
<TargetFrameworks>net9.0;net10.0</TargetFrameworks>
|
||||||
<RootNamespace>lib</RootNamespace>
|
<RootNamespace>lib</RootNamespace>
|
||||||
<AssemblyVersion>0.0.1.0</AssemblyVersion>
|
<AssemblyVersion>0.0.1.0</AssemblyVersion>
|
||||||
<FileVersion>0.0.1.0</FileVersion>
|
<FileVersion>0.0.1.0</FileVersion>
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using imm;
|
|
||||||
|
|
||||||
namespace exp;
|
namespace exp;
|
||||||
|
|
||||||
|
|
||||||
abstract public record class Exp<T> : imm.Versioned<Exp<T>>
|
abstract public record class Exp<T> : io.Versioned<Exp<T>>
|
||||||
{
|
{
|
||||||
protected Exp()
|
protected Exp()
|
||||||
:
|
:
|
||||||
|
|||||||
22
imm/FSM.cs
22
imm/FSM.cs
@ -5,24 +5,24 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base context for an FSM.
|
/// Base context for an FSM.
|
||||||
/// MUST inherit from imm.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
/// MUST inherit from io.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TSelf">The concrete Context type.</typeparam>
|
/// <typeparam name="TSelf">The concrete Context type.</typeparam>
|
||||||
public abstract record class FsmContextBase<TSelf> : imm.Recorded<TSelf>
|
public abstract record class FsmContextBase<TSelf> : io.Recorded<TSelf>
|
||||||
where TSelf : FsmContextBase<TSelf>
|
where TSelf : FsmContextBase<TSelf>
|
||||||
{
|
{
|
||||||
// Required for 'with' expressions.
|
// Required for 'with' expressions.
|
||||||
protected FsmContextBase(imm.Recorded<TSelf> original) : base(original) { }
|
protected FsmContextBase(io.Recorded<TSelf> original) : base(original) { }
|
||||||
protected FsmContextBase() { }
|
protected FsmContextBase() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base state for an FSM.
|
/// Base state for an FSM.
|
||||||
/// MUST inherit from imm.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
/// MUST inherit from io.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TSelf">The concrete State type.</typeparam>
|
/// <typeparam name="TSelf">The concrete State type.</typeparam>
|
||||||
/// <typeparam name="TCtx">The concrete Context type (must be based on FsmContextBase).</typeparam>
|
/// <typeparam name="TCtx">The concrete Context type (must be based on FsmContextBase).</typeparam>
|
||||||
public abstract record class FsmStateBase<TSelf, TCtx> : imm.Recorded<TSelf>
|
public abstract record class FsmStateBase<TSelf, TCtx> : io.Recorded<TSelf>
|
||||||
where TSelf : FsmStateBase<TSelf, TCtx>
|
where TSelf : FsmStateBase<TSelf, TCtx>
|
||||||
where TCtx : FsmContextBase<TCtx>
|
where TCtx : FsmContextBase<TCtx>
|
||||||
{
|
{
|
||||||
@ -43,18 +43,18 @@ public abstract record class FsmStateBase<TSelf, TCtx> : imm.Recorded<TSelf>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Required for 'with' expressions.
|
// Required for 'with' expressions.
|
||||||
protected FsmStateBase(imm.Recorded<TSelf> original) : base(original) { }
|
protected FsmStateBase(io.Recorded<TSelf> original) : base(original) { }
|
||||||
protected FsmStateBase() { }
|
protected FsmStateBase() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An immutable FSM base class.
|
/// An immutable FSM base class.
|
||||||
/// MUST inherit from imm.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
/// MUST inherit from io.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TSelf">The concrete FSM type.</typeparam>
|
/// <typeparam name="TSelf">The concrete FSM type.</typeparam>
|
||||||
/// <typeparam name="TState">The concrete State type.</typeparam>
|
/// <typeparam name="TState">The concrete State type.</typeparam>
|
||||||
/// <typeparam name="TCtx">The concrete Context type.</typeparam>
|
/// <typeparam name="TCtx">The concrete Context type.</typeparam>
|
||||||
public abstract record class FsmBase<TSelf, TState, TCtx> : imm.Recorded<TSelf>
|
public abstract record class FsmBase<TSelf, TState, TCtx> : io.Recorded<TSelf>
|
||||||
where TSelf : FsmBase<TSelf, TState, TCtx>
|
where TSelf : FsmBase<TSelf, TState, TCtx>
|
||||||
where TState : FsmStateBase<TState, TCtx>
|
where TState : FsmStateBase<TState, TCtx>
|
||||||
where TCtx : FsmContextBase<TCtx>
|
where TCtx : FsmContextBase<TCtx>
|
||||||
@ -69,7 +69,7 @@ public abstract record class FsmBase<TSelf, TState, TCtx> : imm.Recorded<TSelf>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Required for 'with' expressions.
|
// Required for 'with' expressions.
|
||||||
protected FsmBase(imm.Recorded<TSelf> original) : base(original)
|
protected FsmBase(io.Recorded<TSelf> original) : base(original)
|
||||||
{
|
{
|
||||||
var o = original as FsmBase<TSelf, TState, TCtx>;
|
var o = original as FsmBase<TSelf, TState, TCtx>;
|
||||||
Context = o!.Context;
|
Context = o!.Context;
|
||||||
@ -78,7 +78,7 @@ public abstract record class FsmBase<TSelf, TState, TCtx> : imm.Recorded<TSelf>
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transitions the FSM. It automatically uses the 'Process'
|
/// Transitions the FSM. It automatically uses the 'Process'
|
||||||
/// method appropriate for imm.Recorded or Timed, thanks to virtual overrides.
|
/// method appropriate for io.Recorded or Timed, thanks to virtual overrides.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TSelf Transition(
|
public TSelf Transition(
|
||||||
TState newState,
|
TState newState,
|
||||||
@ -93,7 +93,7 @@ public abstract record class FsmBase<TSelf, TState, TCtx> : imm.Recorded<TSelf>
|
|||||||
var (ctxAfterExit, stateAfterExit) = State.OnExit(Context, newState);
|
var (ctxAfterExit, stateAfterExit) = State.OnExit(Context, newState);
|
||||||
var (ctxAfterEnter, stateAfterEnter) = newState.OnEnter(ctxAfterExit, stateAfterExit);
|
var (ctxAfterEnter, stateAfterEnter) = newState.OnEnter(ctxAfterExit, stateAfterExit);
|
||||||
|
|
||||||
// Since 'this' is at least 'imm.Recorded<TSelf>', we can call the
|
// Since 'this' is at least 'io.Recorded<TSelf>', we can call the
|
||||||
// detailed 'Process'. If 'this' is actually 'Timed<TSelf>', C#'s
|
// detailed 'Process'. If 'this' is actually 'Timed<TSelf>', C#'s
|
||||||
// virtual dispatch will call the 'Timed' override automatically.
|
// virtual dispatch will call the 'Timed' override automatically.
|
||||||
return Process(
|
return Process(
|
||||||
|
|||||||
375
imm/Imm.cs
375
imm/Imm.cs
@ -1,366 +1,30 @@
|
|||||||
#nullable enable
|
//#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace imm;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the base interface for versioned, immutable objects.
|
/// Helper static class for processing immutable objects using a 'ref' pattern.
|
||||||
/// Provides access to metadata and potentially the previous version.
|
/// Provides different levels of processing based on the type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface Obj
|
public static class imm
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the base metadata associated with this version.
|
/// Processes a 'Versioned' object (Level 1).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Metadata_Versioned Meta { get; }
|
public static T LightProcess<T>(
|
||||||
|
ref T obj,
|
||||||
/// <summary>
|
|
||||||
/// Gets the previous version as a base object, if available.
|
|
||||||
/// Returns null if this is the first version or if history is not tracked.
|
|
||||||
/// </summary>
|
|
||||||
Obj? Old { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new version without functional change.
|
|
||||||
/// Returns the new version as an Obj.
|
|
||||||
/// </summary>
|
|
||||||
Obj Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obj delegate for change notifications.
|
|
||||||
/// </summary>
|
|
||||||
public delegate void ChangeDelegate<T>( T? oldVersion, T newVersion );
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a generic interface for immutable objects,
|
|
||||||
/// providing access to basic processing functions and change notifications.
|
|
||||||
/// </summary>
|
|
||||||
public interface Obj<T> : Obj where T : Obj<T>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the change delegate associated with this object.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
ChangeDelegate<T> OnChange { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a transformation and creates a new version using basic processing.
|
|
||||||
/// </summary>
|
|
||||||
T Process(
|
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
string reason = "Processed",
|
string reason = "Processed" )
|
||||||
[CallerMemberName] string dbgName = "",
|
where T : io.Versioned<T>
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" );
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new version without a functional change using basic processing.
|
|
||||||
/// Uses 'new' to provide a type-safe return.
|
|
||||||
/// </summary>
|
|
||||||
new T Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
static public class ObjExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new version of the object with the specified reason.
|
|
||||||
/// </summary>
|
|
||||||
public static T Record<T>( this T obj, string reason = "Recorded" ) where T : Obj
|
|
||||||
{
|
{
|
||||||
if( obj is Recorded<T> recorded )
|
obj = obj.Process( fn, reason );
|
||||||
{
|
return obj;
|
||||||
return recorded.Record( reason );
|
|
||||||
}
|
|
||||||
else if( obj is Versioned<T> versioned )
|
|
||||||
{
|
|
||||||
return versioned.Record( reason );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Dont care
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --- Metadata Hierarchy ---
|
|
||||||
|
|
||||||
public interface VersionedMeta
|
|
||||||
{
|
|
||||||
public uint Version { get; }
|
|
||||||
public string Reason { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obj metadata for version tracking.
|
|
||||||
/// </summary>
|
|
||||||
public record Metadata_Versioned
|
|
||||||
{
|
|
||||||
public uint Version { get; init; } = 1;
|
|
||||||
public string Reason { get; init; } = "Created";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface RecordedMeta : VersionedMeta
|
|
||||||
{
|
|
||||||
public string MemberName { get; }
|
|
||||||
public string FilePath { get; }
|
|
||||||
public int LineNumber { get; }
|
|
||||||
public string Expression { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Metadata for version and recording (debug/caller info, history).
|
|
||||||
/// </summary>
|
|
||||||
public record Metadata_Recorded : Metadata_Versioned, RecordedMeta
|
|
||||||
{
|
|
||||||
internal object? OldObject { get; init; } = null;
|
|
||||||
public string MemberName { get; init; } = "";
|
|
||||||
public string FilePath { get; init; } = "";
|
|
||||||
public int LineNumber { get; init; } = 0;
|
|
||||||
public string Expression { get; init; } = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface TimedMeta : RecordedMeta
|
|
||||||
{
|
|
||||||
public DateTime CreatedAt { get; }
|
|
||||||
public DateTime TouchedAt { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Metadata for version, recording, and timing.
|
|
||||||
/// </summary>
|
|
||||||
public record Metadata_Timed : Metadata_Recorded, TimedMeta
|
|
||||||
{
|
|
||||||
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
|
|
||||||
public DateTime TouchedAt { get; init; } = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Record Hierarchy ---
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Level 1: Basic versioning. Implements Obj<T>.
|
|
||||||
/// </summary>
|
|
||||||
public record class Versioned<T> : Obj<T> where T : Versioned<T>
|
|
||||||
{
|
|
||||||
public Metadata_Versioned Meta { get; init; } = new();
|
|
||||||
|
|
||||||
[DebuggerBrowsable( DebuggerBrowsableState.Never )]
|
|
||||||
[JsonIgnore]
|
|
||||||
public ChangeDelegate<T> OnChange { get; set; } = ( o, n ) => { };
|
|
||||||
|
|
||||||
public virtual Obj? Old => null;
|
|
||||||
|
|
||||||
Metadata_Versioned Obj.Meta => this.Meta;
|
|
||||||
[JsonIgnore]
|
|
||||||
Obj? Obj.Old => this.Old;
|
|
||||||
|
|
||||||
public Versioned() { }
|
|
||||||
protected Versioned( Versioned<T> original )
|
|
||||||
{
|
|
||||||
OnChange = original.OnChange;
|
|
||||||
Meta = original.Meta;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual T Process(
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "Processed",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
|
||||||
{
|
|
||||||
var current = (T)this;
|
|
||||||
var next = fn( current );
|
|
||||||
|
|
||||||
if( ReferenceEquals( current, next ) )
|
|
||||||
return current;
|
|
||||||
|
|
||||||
var newVersion = next with
|
|
||||||
{
|
|
||||||
Meta = new Metadata_Versioned { /*...*/ },
|
|
||||||
OnChange = current.OnChange
|
|
||||||
};
|
|
||||||
newVersion.OnChange( current, newVersion );
|
|
||||||
return newVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Basic Record. Made virtual. Implements Obj<T>.Record.
|
/// Processes a 'Recorded' object (Level 2), capturing caller info.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual T Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 ) => Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implements Obj.Record by calling the virtual T Record.
|
|
||||||
/// </summary>
|
|
||||||
Obj Obj.Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 ) => this.Record( reason, dbgName, dbgPath, dbgLine );
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Level 2: Adds history and caller info.
|
|
||||||
/// </summary>
|
|
||||||
public record class Recorded<T> : Versioned<T> where T : Recorded<T>
|
|
||||||
{
|
|
||||||
new public Metadata_Recorded Meta { get; init; } = new();
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
new public T? Old => Meta.OldObject as T;
|
|
||||||
|
|
||||||
//public override Obj? Old => this.Old;
|
|
||||||
//Metadata_Versioned Obj.Meta => this.Meta;
|
|
||||||
|
|
||||||
public Recorded() { }
|
|
||||||
protected Recorded( Recorded<T> original ) : base( original ) { Meta = original.Meta; }
|
|
||||||
|
|
||||||
public override T Process(
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
|
||||||
{
|
|
||||||
var current = (T)this;
|
|
||||||
var next = fn( current );
|
|
||||||
|
|
||||||
if( ReferenceEquals( current, next ) )
|
|
||||||
return current;
|
|
||||||
|
|
||||||
var newMeta = current.Meta with {
|
|
||||||
Version = current.Meta.Version + 1,
|
|
||||||
Reason = reason,
|
|
||||||
MemberName = dbgName,
|
|
||||||
FilePath = dbgPath,
|
|
||||||
LineNumber = dbgLine,
|
|
||||||
Expression = expStr,
|
|
||||||
OldObject = current,
|
|
||||||
};
|
|
||||||
|
|
||||||
var newVersion = next with { Meta = newMeta, OnChange = current.OnChange };
|
|
||||||
newVersion.OnChange( current, newVersion );
|
|
||||||
return newVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public new T Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
|
||||||
{
|
|
||||||
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Level 3: Adds timestamps.
|
|
||||||
/// </summary>
|
|
||||||
public record class Timed<T> : Recorded<T> where T : Timed<T>
|
|
||||||
{
|
|
||||||
new public Metadata_Timed Meta { get; init; } = new();
|
|
||||||
//Metadata_Versioned Obj.Meta => this.Meta;
|
|
||||||
public TimeSpan SinceLastTouch => Meta.TouchedAt - ( Old?.Meta as Metadata_Timed )?.TouchedAt ?? TimeSpan.Zero;
|
|
||||||
public TimeSpan TotalAge => Meta.TouchedAt - Meta.CreatedAt;
|
|
||||||
|
|
||||||
public Timed() { }
|
|
||||||
protected Timed( Timed<T> original ) : base( original ) { Meta = original.Meta; }
|
|
||||||
|
|
||||||
|
|
||||||
public override T Process(
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "",
|
|
||||||
[CallerMemberName] string dbgMethod = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string dbgExpStr = "" )
|
|
||||||
{
|
|
||||||
var current = (T)this;
|
|
||||||
var next = fn( current );
|
|
||||||
|
|
||||||
if( ReferenceEquals( current, next ) )
|
|
||||||
return current;
|
|
||||||
|
|
||||||
/*
|
|
||||||
var newMeta = new Metadata_Timed
|
|
||||||
{
|
|
||||||
Version = current.Meta.Version + 1,
|
|
||||||
Reason = reason,
|
|
||||||
MemberName = dbgMethod,
|
|
||||||
FilePath = dbgPath,
|
|
||||||
LineNumber = dbgLine,
|
|
||||||
Expression = dbgExpression,
|
|
||||||
OldObject = current,
|
|
||||||
CreatedAt = current.Meta.CreatedAt,
|
|
||||||
TouchedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
var newMeta = current.Meta with
|
|
||||||
{
|
|
||||||
Version = current.Meta.Version + 1,
|
|
||||||
Reason = reason,
|
|
||||||
MemberName = dbgMethod,
|
|
||||||
FilePath = dbgPath,
|
|
||||||
LineNumber = dbgLine,
|
|
||||||
Expression = dbgExpStr,
|
|
||||||
OldObject = current,
|
|
||||||
TouchedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
// Testing: Shouldnt need the OnChange
|
|
||||||
var newVersion = next with { Meta = newMeta };
|
|
||||||
newVersion.OnChange( current, newVersion );
|
|
||||||
return newVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public new T Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
|
||||||
{
|
|
||||||
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
public static class TimedExt
|
|
||||||
{
|
|
||||||
public static T Process<T>(
|
public static T Process<T>(
|
||||||
ref T obj,
|
ref T obj,
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
@ -368,11 +32,20 @@ public static class TimedExt
|
|||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgName = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
[CallerArgumentExpression( "fn" )] string dbgExpression = ""
|
[CallerArgumentExpression( "fn" )] string dbgExpression = "" )
|
||||||
) where T : imm.Timed<T>
|
where T : io.Recorded<T> // Catches Recorded and Timed
|
||||||
{
|
{
|
||||||
|
// This will call the 'Timed' override if T is Timed,
|
||||||
|
// or the 'Recorded' override if T is Recorded.
|
||||||
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string LogDiff<T>( T cur, T old, [CallerArgumentExpression( "cur" )] string dbgExpCur = "", [CallerArgumentExpression( "old" )] string dbgExpOld = "" )
|
||||||
|
{
|
||||||
|
return $"{dbgExpCur} changed from {old} to {cur}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// No specific Process needed for Timed, as it's caught by Recorded<T>
|
||||||
|
// and its Process override handles the timing.
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace imm;
|
namespace io;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An immutable list implementation that tracks history, metadata, and time.
|
/// An immutable list implementation that tracks history, metadata, and time.
|
||||||
|
|||||||
@ -1,4 +1,2 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace imm;
|
|
||||||
|
|||||||
378
imm/io.cs
Normal file
378
imm/io.cs
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace io;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the base interface for versioned, immutable objects.
|
||||||
|
/// Provides access to metadata and potentially the previous version.
|
||||||
|
/// </summary>
|
||||||
|
public interface Obj
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the base metadata associated with this version.
|
||||||
|
/// </summary>
|
||||||
|
Metadata_Versioned Meta { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the previous version as a base object, if available.
|
||||||
|
/// Returns null if this is the first version or if history is not tracked.
|
||||||
|
/// </summary>
|
||||||
|
Obj? Old { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new version without functional change.
|
||||||
|
/// Returns the new version as an Obj.
|
||||||
|
/// </summary>
|
||||||
|
Obj Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obj delegate for change notifications.
|
||||||
|
/// </summary>
|
||||||
|
public delegate void ChangeDelegate<T>( T? oldVersion, T newVersion );
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a generic interface for immutable objects,
|
||||||
|
/// providing access to basic processing functions and change notifications.
|
||||||
|
/// </summary>
|
||||||
|
public interface Obj<T> : Obj where T : Obj<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the change delegate associated with this object.
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
ChangeDelegate<T> OnChange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a transformation and creates a new version using basic processing.
|
||||||
|
/// </summary>
|
||||||
|
T Process(
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "Processed",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string expStr = "" );
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new version without a functional change using basic processing.
|
||||||
|
/// Uses 'new' to provide a type-safe return.
|
||||||
|
/// </summary>
|
||||||
|
new T Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
static public class ObjExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new version of the object with the specified reason.
|
||||||
|
/// </summary>
|
||||||
|
public static T Record<T>( this T obj, string reason = "Recorded" ) where T : Obj
|
||||||
|
{
|
||||||
|
if( obj is Recorded<T> recorded )
|
||||||
|
{
|
||||||
|
return recorded.Record( reason );
|
||||||
|
}
|
||||||
|
else if( obj is Versioned<T> versioned )
|
||||||
|
{
|
||||||
|
return versioned.Record( reason );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Dont care
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// --- Metadata Hierarchy ---
|
||||||
|
|
||||||
|
public interface VersionedMeta
|
||||||
|
{
|
||||||
|
public uint Version { get; }
|
||||||
|
public string Reason { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obj metadata for version tracking.
|
||||||
|
/// </summary>
|
||||||
|
public record Metadata_Versioned
|
||||||
|
{
|
||||||
|
public uint Version { get; init; } = 1;
|
||||||
|
public string Reason { get; init; } = "Created";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface RecordedMeta : VersionedMeta
|
||||||
|
{
|
||||||
|
public string MemberName { get; }
|
||||||
|
public string FilePath { get; }
|
||||||
|
public int LineNumber { get; }
|
||||||
|
public string Expression { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata for version and recording (debug/caller info, history).
|
||||||
|
/// </summary>
|
||||||
|
public record Metadata_Recorded : Metadata_Versioned, RecordedMeta
|
||||||
|
{
|
||||||
|
internal object? OldObject { get; init; } = null;
|
||||||
|
public string MemberName { get; init; } = "";
|
||||||
|
public string FilePath { get; init; } = "";
|
||||||
|
public int LineNumber { get; init; } = 0;
|
||||||
|
public string Expression { get; init; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface TimedMeta : RecordedMeta
|
||||||
|
{
|
||||||
|
public DateTime CreatedAt { get; }
|
||||||
|
public DateTime TouchedAt { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata for version, recording, and timing.
|
||||||
|
/// </summary>
|
||||||
|
public record Metadata_Timed : Metadata_Recorded, TimedMeta
|
||||||
|
{
|
||||||
|
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
|
||||||
|
public DateTime TouchedAt { get; init; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Record Hierarchy ---
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Level 1: Basic versioning. Implements Obj<T>.
|
||||||
|
/// </summary>
|
||||||
|
public record class Versioned<T> : Obj<T> where T : Versioned<T>
|
||||||
|
{
|
||||||
|
public Metadata_Versioned Meta { get; init; } = new();
|
||||||
|
|
||||||
|
[DebuggerBrowsable( DebuggerBrowsableState.Never )]
|
||||||
|
[JsonIgnore]
|
||||||
|
public ChangeDelegate<T> OnChange { get; set; } = ( o, n ) => { };
|
||||||
|
|
||||||
|
public virtual Obj? Old => null;
|
||||||
|
|
||||||
|
Metadata_Versioned Obj.Meta => this.Meta;
|
||||||
|
[JsonIgnore]
|
||||||
|
Obj? Obj.Old => this.Old;
|
||||||
|
|
||||||
|
public Versioned() { }
|
||||||
|
protected Versioned( Versioned<T> original )
|
||||||
|
{
|
||||||
|
OnChange = original.OnChange;
|
||||||
|
Meta = original.Meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T Process(
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "Processed",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
||||||
|
{
|
||||||
|
var current = (T)this;
|
||||||
|
var next = fn( current );
|
||||||
|
|
||||||
|
if( ReferenceEquals( current, next ) )
|
||||||
|
return current;
|
||||||
|
|
||||||
|
var newVersion = next with
|
||||||
|
{
|
||||||
|
Meta = new Metadata_Versioned { /*...*/ },
|
||||||
|
OnChange = current.OnChange
|
||||||
|
};
|
||||||
|
newVersion.OnChange( current, newVersion );
|
||||||
|
return newVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Basic Record. Made virtual. Implements Obj<T>.Record.
|
||||||
|
/// </summary>
|
||||||
|
public virtual T Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 ) => Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements Obj.Record by calling the virtual T Record.
|
||||||
|
/// </summary>
|
||||||
|
Obj Obj.Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 ) => this.Record( reason, dbgName, dbgPath, dbgLine );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Level 2: Adds history and caller info.
|
||||||
|
/// </summary>
|
||||||
|
public record class Recorded<T> : Versioned<T> where T : Recorded<T>
|
||||||
|
{
|
||||||
|
new public Metadata_Recorded Meta { get; init; } = new();
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
new public T? Old => Meta.OldObject as T;
|
||||||
|
|
||||||
|
//public override Obj? Old => this.Old;
|
||||||
|
//Metadata_Versioned Obj.Meta => this.Meta;
|
||||||
|
|
||||||
|
public Recorded() { }
|
||||||
|
protected Recorded( Recorded<T> original ) : base( original ) { Meta = original.Meta; }
|
||||||
|
|
||||||
|
public override T Process(
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
||||||
|
{
|
||||||
|
var current = (T)this;
|
||||||
|
var next = fn( current );
|
||||||
|
|
||||||
|
if( ReferenceEquals( current, next ) )
|
||||||
|
return current;
|
||||||
|
|
||||||
|
var newMeta = current.Meta with {
|
||||||
|
Version = current.Meta.Version + 1,
|
||||||
|
Reason = reason,
|
||||||
|
MemberName = dbgName,
|
||||||
|
FilePath = dbgPath,
|
||||||
|
LineNumber = dbgLine,
|
||||||
|
Expression = expStr,
|
||||||
|
OldObject = current,
|
||||||
|
};
|
||||||
|
|
||||||
|
var newVersion = next with { Meta = newMeta, OnChange = current.OnChange };
|
||||||
|
newVersion.OnChange( current, newVersion );
|
||||||
|
return newVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new T Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
|
{
|
||||||
|
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Level 3: Adds timestamps.
|
||||||
|
/// </summary>
|
||||||
|
public record class Timed<T> : Recorded<T> where T : Timed<T>
|
||||||
|
{
|
||||||
|
new public Metadata_Timed Meta { get; init; } = new();
|
||||||
|
//Metadata_Versioned Obj.Meta => this.Meta;
|
||||||
|
public TimeSpan SinceLastTouch => Meta.TouchedAt - ( Old?.Meta as Metadata_Timed )?.TouchedAt ?? TimeSpan.Zero;
|
||||||
|
public TimeSpan TotalAge => Meta.TouchedAt - Meta.CreatedAt;
|
||||||
|
|
||||||
|
public Timed() { }
|
||||||
|
protected Timed( Timed<T> original ) : base( original ) { Meta = original.Meta; }
|
||||||
|
|
||||||
|
|
||||||
|
public override T Process(
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "",
|
||||||
|
[CallerMemberName] string dbgMethod = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string dbgExpStr = "" )
|
||||||
|
{
|
||||||
|
var current = (T)this;
|
||||||
|
var next = fn( current );
|
||||||
|
|
||||||
|
if( ReferenceEquals( current, next ) )
|
||||||
|
return current;
|
||||||
|
|
||||||
|
/*
|
||||||
|
var newMeta = new Metadata_Timed
|
||||||
|
{
|
||||||
|
Version = current.Meta.Version + 1,
|
||||||
|
Reason = reason,
|
||||||
|
MemberName = dbgMethod,
|
||||||
|
FilePath = dbgPath,
|
||||||
|
LineNumber = dbgLine,
|
||||||
|
Expression = dbgExpression,
|
||||||
|
OldObject = current,
|
||||||
|
CreatedAt = current.Meta.CreatedAt,
|
||||||
|
TouchedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
var newMeta = current.Meta with
|
||||||
|
{
|
||||||
|
Version = current.Meta.Version + 1,
|
||||||
|
Reason = reason,
|
||||||
|
MemberName = dbgMethod,
|
||||||
|
FilePath = dbgPath,
|
||||||
|
LineNumber = dbgLine,
|
||||||
|
Expression = dbgExpStr,
|
||||||
|
OldObject = current,
|
||||||
|
TouchedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
// Testing: Shouldnt need the OnChange
|
||||||
|
var newVersion = next with { Meta = newMeta };
|
||||||
|
newVersion.OnChange( current, newVersion );
|
||||||
|
return newVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new T Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
|
{
|
||||||
|
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public static class TimedExt
|
||||||
|
{
|
||||||
|
public static T Process<T>(
|
||||||
|
ref T obj,
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string dbgExpression = ""
|
||||||
|
) where T : io.Timed<T>
|
||||||
|
{
|
||||||
|
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
52
imm/iu.cs
52
imm/iu.cs
@ -1,52 +0,0 @@
|
|||||||
//#nullable enable
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using imm;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper static class for processing immutable objects using a 'ref' pattern.
|
|
||||||
/// Provides different levels of processing based on the type.
|
|
||||||
/// </summary>
|
|
||||||
public static class iu
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Processes a 'Versioned' object (Level 1).
|
|
||||||
/// </summary>
|
|
||||||
public static T LightProcess<T>(
|
|
||||||
ref T obj,
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "Processed" )
|
|
||||||
where T : Versioned<T>
|
|
||||||
{
|
|
||||||
obj = obj.Process( fn, reason );
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Processes a 'Recorded' object (Level 2), capturing caller info.
|
|
||||||
/// </summary>
|
|
||||||
public static T Process<T>(
|
|
||||||
ref T obj,
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string dbgExpression = "" )
|
|
||||||
where T : Recorded<T> // Catches Recorded and Timed
|
|
||||||
{
|
|
||||||
// This will call the 'Timed' override if T is Timed,
|
|
||||||
// or the 'Recorded' override if T is Recorded.
|
|
||||||
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string LogDiff<T>( T cur, T old, [CallerArgumentExpression( "cur" )] string dbgExpCur = "", [CallerArgumentExpression( "old" )] string dbgExpOld = "" )
|
|
||||||
{
|
|
||||||
return $"{dbgExpCur} changed from {old} to {cur}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// No specific Process needed for Timed, as it's caught by Recorded<T>
|
|
||||||
// and its Process override handles the timing.
|
|
||||||
}
|
|
||||||
@ -17,7 +17,7 @@ using System.Collections.Immutable;
|
|||||||
|
|
||||||
namespace ser;
|
namespace ser;
|
||||||
|
|
||||||
public record CodeGenConfig : imm.Recorded<CodeGenConfig>
|
public record CodeGenConfig : io.Recorded<CodeGenConfig>
|
||||||
{
|
{
|
||||||
// Whitelists (if needed, otherwise rely on attributes/defaults)
|
// Whitelists (if needed, otherwise rely on attributes/defaults)
|
||||||
public ImmutableDictionary<string, ImmutableList<string>> WLProps { get; init; } = ImmutableDictionary<string, ImmutableList<string>>.Empty;
|
public ImmutableDictionary<string, ImmutableList<string>> WLProps { get; init; } = ImmutableDictionary<string, ImmutableList<string>>.Empty;
|
||||||
|
|||||||
@ -490,7 +490,7 @@ namespace lib
|
|||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"" );
|
if( _cfg.VerboseLogging ) log.info( $"" );
|
||||||
|
|
||||||
var isImm = typeof(imm.Obj).IsAssignableFrom( narrowType );
|
var isImm = typeof(io.Obj).IsAssignableFrom( narrowType );
|
||||||
|
|
||||||
XmlNodeList allChildren = elem.ChildNodes;
|
XmlNodeList allChildren = elem.ChildNodes;
|
||||||
|
|
||||||
@ -648,8 +648,8 @@ namespace lib
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var imm = obj as imm.Obj;
|
var immObj = obj as io.Obj;
|
||||||
var newObj = imm.Record( $"From XML {fromStr}:{elem.ParentNode?.Name}{elem.Name}" );
|
var newObj = immObj.Record( $"From XML {fromStr}:{elem.ParentNode?.Name}{elem.Name}" );
|
||||||
return newObj;
|
return newObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1290,7 +1290,7 @@ namespace lib
|
|||||||
HashSet<string> whitelistFields, whitelistProps;
|
HashSet<string> whitelistFields, whitelistProps;
|
||||||
GetFilters( _cfg.TypesDefault, mi, narrowType, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps );
|
GetFilters( _cfg.TypesDefault, mi, narrowType, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps );
|
||||||
|
|
||||||
var isImm = typeof(imm.Obj).IsAssignableFrom( narrowType );
|
var isImm = typeof(io.Obj).IsAssignableFrom( narrowType );
|
||||||
|
|
||||||
if( doFields || doImpls )
|
if( doFields || doImpls )
|
||||||
{
|
{
|
||||||
|
|||||||
@ -32,6 +32,7 @@ N O T D O I N G :
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
public record struct Value<T>( T _val, string _exp = "" )
|
public record struct Value<T>( T _val, string _exp = "" )
|
||||||
{
|
{
|
||||||
public static T Default = default!;
|
public static T Default = default!;
|
||||||
@ -87,13 +88,10 @@ public struct SourceLoc
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endregion // Helpers
|
||||||
|
|
||||||
static public class log
|
static public class log
|
||||||
{
|
{
|
||||||
|
|
||||||
//static
|
|
||||||
|
|
||||||
#region CLR Logging
|
#region CLR Logging
|
||||||
|
|
||||||
|
|
||||||
@ -155,14 +153,6 @@ static public class log
|
|||||||
|
|
||||||
#endregion // CLR Logging
|
#endregion // CLR Logging
|
||||||
|
|
||||||
static public Value<T> Value<T>( T val,
|
|
||||||
[CallerArgumentExpression("val")]
|
|
||||||
string dbgExp = ""
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return new( val, dbgExp );
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum LogType
|
public enum LogType
|
||||||
{
|
{
|
||||||
@ -184,13 +174,15 @@ static public class log
|
|||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
||||||
File = 1 << 0,
|
File = 1 << 0,
|
||||||
Console = 1 << 1,
|
Console = 1 << 1,
|
||||||
|
|
||||||
All = File | Console,
|
All = File | Console,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region LogEvent
|
||||||
|
|
||||||
public struct LogEvent
|
public struct LogEvent
|
||||||
{
|
{
|
||||||
public DateTime Time;
|
public DateTime Time;
|
||||||
@ -264,7 +256,7 @@ static public class log
|
|||||||
ImmutableInterlocked.AddOrUpdate( ref s_logEPforCat, cat, ep, ( k, v ) => ep );
|
ImmutableInterlocked.AddOrUpdate( ref s_logEPforCat, cat, ep, ( k, v ) => ep );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion // LogEvent
|
||||||
|
|
||||||
static public void shutdown()
|
static public void shutdown()
|
||||||
{
|
{
|
||||||
@ -1020,4 +1012,13 @@ static public class log
|
|||||||
|
|
||||||
private static ArrayList s_delegates = new ArrayList();
|
private static ArrayList s_delegates = new ArrayList();
|
||||||
|
|
||||||
}
|
static public Value<T> Value<T>( T val,
|
||||||
|
[CallerArgumentExpression("val")]
|
||||||
|
string dbgExp = ""
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return new( val, dbgExp );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // end static class log
|
||||||
|
|||||||
@ -182,7 +182,7 @@ public class Ref<T> : Ref where T : class, new()
|
|||||||
// Let's assume you'll add saving logic here.
|
// Let's assume you'll add saving logic here.
|
||||||
// Mgr.Save(value, path); // Example: Needs implementation
|
// Mgr.Save(value, path); // Example: Needs implementation
|
||||||
|
|
||||||
var immMeta = (value as imm.Obj)?.Meta;
|
var immMeta = (value as io.Obj)?.Meta;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -433,10 +433,10 @@ public static class Mgr
|
|||||||
var loadedObject = loaderHolder.Load( filename, reason, dbgName, dbgPath, dbgLine );
|
var loadedObject = loaderHolder.Load( filename, reason, dbgName, dbgPath, dbgLine );
|
||||||
if( loadedObject is T value )
|
if( loadedObject is T value )
|
||||||
{
|
{
|
||||||
var meta = (value as imm.Obj)?.Meta;
|
var meta = (value as io.Obj)?.Meta;
|
||||||
|
|
||||||
// If it's an immutable object, record its loading.
|
// If it's an immutable object, record its loading.
|
||||||
if( value is imm.Obj imm )
|
if( value is io.Obj imm )
|
||||||
{
|
{
|
||||||
return (T)imm.Record( $"Loading bcs {reason}", dbgName, dbgPath, dbgLine );
|
return (T)imm.Record( $"Loading bcs {reason}", dbgName, dbgPath, dbgLine );
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,7 +90,7 @@ public enum BackingFieldNaming { Short, Regular }
|
|||||||
public enum POD { Attributes, Elements }
|
public enum POD { Attributes, Elements }
|
||||||
public record struct TypeProxy( Func<object, string> fnSer, Func<string, string, object> fnDes );
|
public record struct TypeProxy( Func<object, string> fnSer, Func<string, string, object> fnDes );
|
||||||
|
|
||||||
public record XmlCfg : imm.Recorded<XmlCfg>
|
public record XmlCfg : io.Recorded<XmlCfg>
|
||||||
{
|
{
|
||||||
public bool Verbose { get; init; } = false;
|
public bool Verbose { get; init; } = false;
|
||||||
public Datastructure Structure { get; init; } = Datastructure.Tree;
|
public Datastructure Structure { get; init; } = Datastructure.Tree;
|
||||||
@ -254,7 +254,7 @@ public class TypeMetaCache
|
|||||||
GetFilters( _cfg.TypesDefault, type, out doImpls, out doFields, out doProps );
|
GetFilters( _cfg.TypesDefault, type, out doImpls, out doFields, out doProps );
|
||||||
|
|
||||||
|
|
||||||
var isImm = typeof( imm.Obj ).IsAssignableFrom( type );
|
var isImm = typeof( io.Obj ).IsAssignableFrom( type );
|
||||||
|
|
||||||
var typesAtt = type.GetCustomAttribute<ser.Ser>( true );
|
var typesAtt = type.GetCustomAttribute<ser.Ser>( true );
|
||||||
var serTypes = typesAtt?.Types ?? ser.Types.None;
|
var serTypes = typesAtt?.Types ?? ser.Types.None;
|
||||||
|
|||||||
@ -179,7 +179,7 @@ public partial class ObjectHandler : ITypeHandler
|
|||||||
// 3. Post-processing
|
// 3. Post-processing
|
||||||
if( obj is ser.I_Serialize iSer )
|
if( obj is ser.I_Serialize iSer )
|
||||||
obj = iSer.OnDeserialize( null );
|
obj = iSer.OnDeserialize( null );
|
||||||
if( ti.IsImm && obj is imm.Obj immObj )
|
if( ti.IsImm && obj is io.Obj immObj )
|
||||||
return immObj.Record( $"From XML {elem.Name}" );
|
return immObj.Record( $"From XML {elem.Name}" );
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ using System.Text;
|
|||||||
namespace ser;
|
namespace ser;
|
||||||
|
|
||||||
|
|
||||||
public record class SimpleImmutable( string Name, int Age ) : imm.Timed<SimpleImmutable>;
|
public record class SimpleImmutable( string Name, int Age ) : io.Timed<SimpleImmutable>;
|
||||||
|
|
||||||
static public class Test
|
static public class Test
|
||||||
{
|
{
|
||||||
|
|||||||
@ -10,7 +10,7 @@ using System.IO;
|
|||||||
namespace test;
|
namespace test;
|
||||||
|
|
||||||
|
|
||||||
public record class SimpleImmutable( string Name, int Age ) : imm.Timed<SimpleImmutable>;
|
public record class SimpleImmutable( string Name, int Age ) : io.Timed<SimpleImmutable>;
|
||||||
|
|
||||||
static public class XmlFormatter2
|
static public class XmlFormatter2
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user