PowerShellUtils/Models/PrintNode.cs

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using PowerShellStandardModule1.Lib.Extensions;
 
namespace PowerShellStandardModule1.Models;
 
public enum Indents
{
    None,
    Middle,
    Last,
    Padding,
    PaddedBranch,
}
 
public static class IndentExtensions
{
    public static string Value(this Indents indent) =>
        indent switch
        {
            Indents.None => "",
            Indents.Middle => "├── ",
            Indents.Last => "└── ",
            Indents.Padding => " ",
            Indents.PaddedBranch => "│ ",
            _ => throw new ArgumentOutOfRangeException(nameof(indent), indent, null)
        };
 
    public static Indents Parse(string value) =>
        value switch
        {
            "" => Indents.None,
            "├── " => Indents.Middle,
            "└── " => Indents.Last,
            " " => Indents.Padding,
            "│ " => Indents.PaddedBranch,
            _ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
        };
 
    public static string ToValueString(this IEnumerable<Indents> indents) =>
        indents
           .Select(x => x.Value())
           .StringJoin("");
}
 
public class PrintNode<T>
{
    public required TreeNode<T> Value;
    public int Index;
 
    public PrintNode<T>? Parent;
    public bool IsRoot => Parent == null;
    private IImmutableList<Indents> Indent { get; set; } = [];
 
 
    public Func<TreeNode<T>, string> StringValueSelector = PrintNode.DefaultStringValueSelector;
 
 
    private static Func<PrintNode<T>, TreeNode<T>, int, PrintNode<T>> ChildConstructor => DefaultChildConstructor;
 
 
    public Func<PrintNode<T>, IEnumerable<TreeNode<T>>> ChildProvider = PrintNode.DefaultChildProvider;
 
    public string StringValue => StringValueSelector(Value);
 
    // if iterating over children in reverse order, last child is now index 0
    private bool IsLast => Index == 0;
 
    private Indents Prefix =>
        IsLast
            ? Indents.Last
            : Indents.Middle;
 
    public string Line =>
        IsRoot
            ? StringValue
            : CompiledIndent.ToValueString() + StringValue;
 
    public IImmutableList<Indents> CompiledIndent =>
        IsRoot
            ? []
            : Indent.Add(Prefix);
 
 
    private Indents PaddingBranch =>
        IsRoot
            ? Indents.None
            : IsLast
                ? Indents.Padding
                : Indents.PaddedBranch;
 
 
    // if it's the last child of its parent, then do not have line in padding
    private IImmutableList<Indents> NextIndent => Indent.Add(PaddingBranch);
 
    public IEnumerable<PrintNode<T>> Children =>
        ChildProvider(this)
           .Reverse()
           .Select(CreateChild);
 
    private PrintNode<T> CreateChild(TreeNode<T> node, int index)
    {
        // override private properties
        var child = ChildConstructor(this, node, index);
        child.Indent = NextIndent;
        return child;
    }
 
    public PrintNode<T> Clone()
    {
        return (PrintNode<T>)MemberwiseClone();
    }
 
    private static PrintNode<T> DefaultChildConstructor(PrintNode<T> parent, TreeNode<T> node, int index)
    {
        var n = parent.Clone();
        n.Value = node;
        n.Index = index;
        n.Parent = parent;
        return n;
    }
         
}
 
public static class PrintNode
{
    public static PrintNode<T> ToPrintNode<T>(this TreeNode<T> node) => From(node);
 
    public static PrintNode<T> From<T>(TreeNode<T> node) => new() { Value = node};
 
    public static string DefaultStringValueSelector<T>(AbstractNode<T> node) =>
        node.Value?.ToString() ?? Indents.None.Value();
 
    public static IEnumerable<TreeNode<T>> DefaultChildProvider<T>(PrintNode<T> node) => node.Value.Children;
}