add Commands and Union

This commit is contained in:
Michael Becker 2024-12-13 00:08:06 -05:00
parent 7c4b71e091
commit 96fcd38e88
10 changed files with 457 additions and 1 deletions

View File

@ -1,7 +1,10 @@
namespace MBS.Core;
using System.ComponentModel.Design;
namespace MBS.Core;
public class Application
{
public IList<Command> Commands { get; } = new List<Command>();
public static Application? Instance { get; private set; }
public CommandLine CommandLine { get; } = new CommandLine();
@ -43,4 +46,31 @@ public class Application
StopInternal(exitCode);
}
private Dictionary<Union<Guid, string>, List<CommandEventHandler>> _commandEventHandlers = new Dictionary<Union<Guid, string>, List<CommandEventHandler>>();
public void AttachCommandEventHandler(Union<Guid, string> commandId, CommandEventHandler handler)
{
if (!_commandEventHandlers.ContainsKey(commandId))
{
_commandEventHandlers[commandId] = new List<CommandEventHandler>();
}
if (!_commandEventHandlers[commandId].Contains(handler))
{
_commandEventHandlers[commandId].Add(handler);
}
}
public void ExecuteCommand(Union<Guid, string> commandId)
{
if (_commandEventHandlers.ContainsKey(commandId))
{
foreach (CommandEventHandler handler in _commandEventHandlers[commandId])
{
handler(this, new CommandEventArgs(commandId));
}
}
else
{
Console.WriteLine("command event handler not registered for command '{0}'", commandId);
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of editor-dotnet.
//
// editor-dotnet is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// editor-dotnet is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with editor-dotnet. If not, see <https://www.gnu.org/licenses/>.
namespace MBS.Core;
public class Command
{
public Command(Union<string, Guid> id, string title)
{
ID = id;
Title = title;
}
public Union<string, Guid> ID { get; }
public string Title { get; set; }
public IEnumerable<CommandItem> Items { get; set; }
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
*
* This file is part of editor-dotnet.
*
* editor-dotnet is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* editor-dotnet is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with editor-dotnet. If not, see <https://www.gnu.org/licenses/>.
*/
namespace MBS.Core;
public delegate void CommandEventHandler(object sender, CommandEventArgs e);
public class CommandEventArgs : EventArgs
{
public Union<Guid, string> CommandID { get; }
public CommandEventArgs(Union<Guid, string> commandId)
{
CommandID = commandId;
}
}

View File

@ -0,0 +1,25 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of editor-dotnet.
//
// editor-dotnet is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// editor-dotnet is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with editor-dotnet. If not, see <https://www.gnu.org/licenses/>.
namespace MBS.Core;
/// <summary>
/// The base class from which all command items derive.
/// </summary>
public abstract class CommandItem
{
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of editor-dotnet.
//
// editor-dotnet is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// editor-dotnet is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with editor-dotnet. If not, see <https://www.gnu.org/licenses/>.
namespace MBS.Core;
public class CommandReferenceCommandItem : CommandItem
{
public Union<Guid, string> CommandID { get; }
public CommandReferenceCommandItem(Union<Guid, string> commandId)
{
CommandID = commandId;
}
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of editor-dotnet.
//
// editor-dotnet is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// editor-dotnet is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with editor-dotnet. If not, see <https://www.gnu.org/licenses/>.
namespace MBS.Core;
/// <summary>
/// Defines a command item which separates groups of related command items.
/// </summary>
public class SeparatorCommandItem : CommandItem
{
}

View File

@ -0,0 +1,189 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of editor-dotnet.
//
// editor-dotnet is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// editor-dotnet is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with editor-dotnet. If not, see <https://www.gnu.org/licenses/>.
using System.Diagnostics.CodeAnalysis;
public struct Union<T1, T2>
{
private T1 _value1;
private T2 _value2;
private int _which;
private bool _notEmpty;
public bool IsEmpty { get { return !_notEmpty; } }
public override string ToString()
{
if (_which == 1)
{
return _value1?.ToString() ?? "";
}
else if (_which == 2)
{
return _value2?.ToString() ?? "";
}
throw new InvalidOperationException("internal error: _which state inconsistent!");
}
private Union(T1 value1, T2 value2, int which)
{
_value1 = value1;
_value2 = value2;
_which = which;
_notEmpty = true;
}
public static implicit operator Union<T1, T2>(T1 value1)
{
return new Union<T1, T2>(value1, default(T2), 1);
}
public static implicit operator Union<T1, T2>(T2 value2)
{
return new Union<T1, T2>(default(T1), value2, 2);
}
public static implicit operator T1(Union<T1, T2> union)
{
if (union._which == 1)
{
return union._value1;
}
throw new InvalidOperationException();
}
public static implicit operator T2(Union<T1, T2> union)
{
if (union._which == 2)
{
return union._value2;
}
throw new InvalidOperationException();
}
public static implicit operator Union<T1, T2>(Union<T2, T1> other)
{
return new Union<T1, T2>(other._value2, other._value1, other._which == 2 ? 1 : 2);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj is Union<T1, T2>)
{
return (this.GetValue()?.Equals(((Union<T1, T2>)obj).GetValue())).GetValueOrDefault(false);
}
else if (obj is T1)
{
return (_value1?.Equals((T1)obj)).GetValueOrDefault(false);
}
else if (obj is T2)
{
return (_value2?.Equals((T2)obj)).GetValueOrDefault(false);
}
return false;
}
private object? GetValue()
{
if (_which == 1)
return _value1;
else if (_which == 2)
return _value2;
throw new InvalidOperationException();
}
}
public struct Union<T1, T2, T3>
{
private object _value;
private bool _notEmpty;
public bool IsEmpty { get { return !_notEmpty; } }
public override string ToString()
{
return _value?.ToString();
}
private Union(object value)
{
_value = value;
_notEmpty = true;
}
public static implicit operator Union<T1, T2, T3>(T1 value)
{
return new Union<T1, T2, T3>(value);
}
public static implicit operator Union<T1, T2, T3>(T2 value)
{
return new Union<T1, T2, T3>(value);
}
public static implicit operator Union<T1, T2, T3>(T3 value)
{
return new Union<T1, T2, T3>(value);
}
public static implicit operator T1(Union<T1, T2, T3> union)
{
if (union._value is T1)
{
return (T1)union._value;
}
throw new InvalidOperationException();
}
public static implicit operator T2(Union<T1, T2, T3> union)
{
if (union._value is T2)
{
return (T2)union._value;
}
throw new InvalidOperationException();
}
public static implicit operator T3(Union<T1, T2, T3> union)
{
if (union._value is T3)
{
return (T3)union._value;
}
throw new InvalidOperationException();
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj is Union<T1, T2, T3>)
{
return (this.GetValue()?.Equals(((Union<T1, T2, T3>)obj).GetValue())).GetValueOrDefault(false);
}
else if (obj is T1)
{
return (_value?.Equals((T1)obj)).GetValueOrDefault(false);
}
else if (obj is T2)
{
return (_value?.Equals((T2)obj)).GetValueOrDefault(false);
}
else if (obj is T3)
{
return (_value?.Equals((T3)obj)).GetValueOrDefault(false);
}
return false;
}
private object? GetValue()
{
return _value;
}
}

View File

@ -0,0 +1 @@
global using NUnit.Framework;

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\MBS.Core\MBS.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,72 @@
using System.Diagnostics.CodeAnalysis;
namespace MBS.Core.Tests;
public class UnionTests
{
public class Union2
{
[SetUp]
public void Setup()
{
}
public const string TEST_VALUE_STR = "Test value";
public const int TEST_VALUE_INT = 314159;
[Test]
public void OperatorEqualsToStringLiteral()
{
Union<string, int> value = TEST_VALUE_STR;
Assert.That(value == TEST_VALUE_STR);
}
[Test]
public void MethodEqualsToStringLiteral()
{
Union<string, int> value = TEST_VALUE_STR;
Assert.That(value.Equals(TEST_VALUE_STR));
}
}
public class Union3
{
[SetUp]
public void Setup()
{
}
public const string TEST_VALUE_STR = "Test value";
public const int TEST_VALUE_INT = 314159;
[Test]
public void OperatorEqualsToInt32Literal()
{
Union<string, int, double> value = TEST_VALUE_INT;
Assert.That(value == TEST_VALUE_INT);
}
[Test]
public void MethodEqualsToStringLiteral()
{
Union<string, int, double> value = TEST_VALUE_STR;
Assert.That(value.Equals(TEST_VALUE_STR));
}
[Test]
public void OperatorEqualsToDoubleLiteral()
{
Union<string, int, double> value = 0.53;
Assert.That(value == 0.53);
}
[Test]
public void MethodCallWithUnion()
{
Union<string, int, double> value = 25.75;
PrintDoubleValue(value);
}
private void PrintDoubleValue(double value)
{
Assert.That(value, Is.EqualTo(25.75));
}
}
}