Programs now built using IProgram interface

- The old App abstract class has been scrapped in favor of the IProgram interface in the Tomas.Interface library.
- A new terminal emulator was created when not on Windows.
- In both OS and terminal, a single dictionary has the commands and classes that implement IProgram.
- Start() now returns a boolean that behaves similar to C's main in returning an integer.
- A new while loop in both terminal and kernel handles the execution of programs.
This commit is contained in:
Tony Bark 2021-03-30 08:26:18 -04:00
parent 952554b476
commit c11f987521
31 changed files with 387 additions and 219 deletions

13
src/.idea/.idea.TOMAS/.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/projectSettingsUpdater.xml
/.idea.TOMAS.iml
/modules.xml
/contentModel.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

1
src/.idea/.idea.TOMAS/.idea/.name generated Normal file
View file

@ -0,0 +1 @@
TOMAS

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ContentModelUserStore">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RIDER_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../../../Changelog.md" />
<content url="file://$MODULE_DIR$/../../../README.md" />
<content url="file://$MODULE_DIR$/../.." />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
src/.idea/.idea.TOMAS/.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View file

@ -15,6 +15,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
TOMAS.sln.licenseheader = TOMAS.sln.licenseheader
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomas.Interface", "Tomas.Interface\Tomas.Interface.csproj", "{DAA9EDF4-83C7-4271-9805-FD6CE29E1796}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomas.Terminal", "Tomas.Terminal\Tomas.Terminal.csproj", "{49E67E55-F9D2-419A-8097-38F39E98A95E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -29,6 +33,14 @@ Global
{C50F3A6F-CFF4-4725-A1A5-21C5A2BC3321}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C50F3A6F-CFF4-4725-A1A5-21C5A2BC3321}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C50F3A6F-CFF4-4725-A1A5-21C5A2BC3321}.Release|Any CPU.Build.0 = Release|Any CPU
{DAA9EDF4-83C7-4271-9805-FD6CE29E1796}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DAA9EDF4-83C7-4271-9805-FD6CE29E1796}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DAA9EDF4-83C7-4271-9805-FD6CE29E1796}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DAA9EDF4-83C7-4271-9805-FD6CE29E1796}.Release|Any CPU.Build.0 = Release|Any CPU
{49E67E55-F9D2-419A-8097-38F39E98A95E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49E67E55-F9D2-419A-8097-38F39E98A95E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49E67E55-F9D2-419A-8097-38F39E98A95E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49E67E55-F9D2-419A-8097-38F39E98A95E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,13 @@
namespace Tomas.Common
{
public struct ComConsts
{
/// <summary>
/// Name of the operating system
/// </summary>
public const string NAME = "TOMAS";
public static string Version = $"{ThisAssembly.Git.SemVer.Major}.{ThisAssembly.Git.SemVer.Minor}.{ThisAssembly.Git.SemVer.Patch}";
public static string VersionGit = $"{Version}-{ThisAssembly.Git.Commit}";
}
}

View file

@ -0,0 +1 @@
0.1

View file

@ -0,0 +1,14 @@
using System;
using Tomas.Interface.Shell;
namespace Tomas.Kernel.Programs
{
public class Clear : IProgram
{
public bool Start()
{
Console.Clear();
return true;
}
}
}

View file

@ -0,0 +1,33 @@
using System;
using Tomas.Interface.Shell;
namespace Tomas.Kernel.Programs
{
public class FenSay : IProgram
{
/// <summary>
/// Fennec art by Todd Vargo
/// </summary>
const string FENNEC = @" \/
/\ /\
//\\_//\\ ____
\_ _/ / /
/ * * \ /^^^]
\_\O/_/ [ ]
/ \_ [ /
\ \_ / /
[ [ / \/ _/
_[ [ \ /_/";
readonly string[] _fenPhrases = { "Screams in fennec!", "Some people call me a coffee fox." };
public bool Start()
{
var rng = new Random();
var phrases = _fenPhrases[rng.Next(0, _fenPhrases.Length)];
Console.WriteLine($"{phrases}{Environment.NewLine}{FENNEC}");
return true;
}
}
}

View file

@ -4,4 +4,15 @@
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Tomas.Interface\Tomas.Interface.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="GitInfo" Version="2.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,7 @@
namespace Tomas.Interface.Shell
{
public interface IProgram
{
bool Start();
}
}

View file

@ -0,0 +1,7 @@
namespace Tomas.Interface.Shell
{
public interface IShell
{
string ReadLine { get; }
}
}

View file

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>

View file

@ -1,35 +0,0 @@
// TOMAS is licensed under the MPL 2.0 license.
// See the LICENSE file in the project root for more information.
using System;
namespace Tomas.Kernel
{
internal class EasterEggs
{
/// <summary>
/// Fennec art by Todd Vargo
/// </summary>
const string FENNEC = @" \/
/\ /\
//\\_//\\ ____
\_ _/ / /
/ * * \ /^^^]
\_\O/_/ [ ]
/ \_ [ /
\ \_ / /
[ [ / \/ _/
_[ [ \ /_/";
static readonly string[] _fenPhrases = { "Screams in fennec!", "Some people call me a coffee fox." };
public static string FenSay
{
get
{
var rng = new Random();
var phrases = _fenPhrases[rng.Next(0, _fenPhrases.Length)];
return $"{phrases}{Environment.NewLine}{FENNEC}";
}
}
}
}

View file

@ -1 +0,0 @@
0.1.0

View file

@ -1,15 +1,17 @@
// TOMAS is licensed under the MPL 2.0 license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using Tomas.Common;
using Tomas.Interface.Shell;
using Tomas.Kernel.Programs;
using Tomas.Terminal.Programs;
using Sys = Cosmos.System;
namespace Tomas.Kernel
{
public class Kernel : Sys.Kernel
{
public bool InApp { get; set; }
protected override void BeforeRun()
{
try
@ -29,23 +31,21 @@ namespace Tomas.Kernel
protected override void Run()
{
var input = Terminal.ReadLine("1) About");
switch (input.ToLowerInvariant())
while (true)
{
case "1":
var basic = new AboutApp(this);
basic.Start();
break;
default:
break;
var shell = new Shell();
var command = shell.ReadLine;
OSConsts.Programs.TryGetValue(command, out var program);
var isRun = program.Start();
if (isRun) continue;
break;
}
}
protected override void AfterRun()
{
if (!InApp)
Console.WriteLine($"{OSConsts.NAME} is shutting down.");
Console.WriteLine($"{ComConsts.NAME} is shutting down.");
}
}
}

View file

@ -1,15 +1,20 @@
// TOMAS is licensed under the MPL 2.0 license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using Tomas.Interface.Shell;
using Tomas.Kernel.Programs;
using Tomas.Terminal.Programs;
namespace Tomas.Kernel
{
public struct OSConsts
{
/// <summary>
/// Name of the operating system
/// </summary>
public const string NAME = "TOMAS";
public static string Version = $"{ThisAssembly.Git.SemVer.Major}.{ThisAssembly.Git.SemVer.Minor}.{ThisAssembly.Git.SemVer.Patch}";
public static string VersionGit = $"{Version}-{ThisAssembly.Git.Commit}";
public static Dictionary<string, IProgram> Programs => new Dictionary<string, IProgram>()
{
{"about", new About()},
{"fensay", new FenSay()},
{"clear", new Clear()}
};
}
}

View file

@ -0,0 +1,21 @@
using System;
using Tomas.Common;
using Tomas.Interface.Shell;
using Tomas.Kernel;
namespace Tomas.Terminal.Programs
{
public class About : IProgram
{
public bool Start()
{
Console.WriteLine($"{ComConsts.NAME} v{ComConsts.VersionGit}");
var progs = OSConsts.Programs;
foreach (var commands in progs.Keys)
Console.WriteLine(commands);
return true;
}
}
}

View file

@ -1,21 +0,0 @@
// TOMAS is licensed under the MPL 2.0 license.
// See the LICENSE file in the project root for more information.
using System;
namespace Tomas.Kernel.Programs
{
class AboutApp : App
{
public AboutApp(Kernel system) : base(system)
{
}
public override void Start()
{
Console.WriteLine($"{OSConsts.NAME} v{OSConsts.VersionGit}");
Console.WriteLine("TOMAS, an abbreviation of Tony's Managed Operating System, is a operating system written in C# using the COSMOS framework.");
base.Start();
}
}
}

View file

@ -1,32 +0,0 @@
// TOMAS is licensed under the MPL 2.0 license.
// See the LICENSE file in the project root for more information.
using sys = Cosmos.System;
namespace Tomas.Kernel.Programs
{
/// <summary>
/// Basic framework for building terminal applications.
/// </summary>
public abstract class App
{
protected App(Kernel system)
{
System = system;
}
Kernel System { get; set; }
/// <summary>
/// Main entry point of the program
/// </summary>
public virtual void Start()
{
System.InApp = true;
var isRKey = sys.KeyboardManager.ReadKey().Key == sys.ConsoleKeyEx.R;
if (sys.KeyboardManager.ControlPressed && isRKey)
System.Restart();
}
}
}

25
src/Tomas.Kernel/Shell.cs Normal file
View file

@ -0,0 +1,25 @@
// TOMAS is licensed under the MPL 2.0 license.
// See the LICENSE file in the project root for more information.
using System;
using Tomas.Common;
using Tomas.Interface.Shell;
using Tomas.Kernel.Programs;
using Sys = Cosmos.System;
namespace Tomas.Kernel
{
public class Shell : IShell
{
const char SYMBOL = '$';
public string ReadLine
{
get
{
Console.Write(SYMBOL);
var readl = Console.ReadLine();
return readl;
}
}
}
}

View file

@ -1,84 +0,0 @@
// TOMAS is licensed under the MPL 2.0 license.
// See the LICENSE file in the project root for more information.
using System;
using Sys = Cosmos.System;
namespace Tomas.Kernel
{
public class Terminal
{
const char SYMBOL = '$';
static void Commands(string command)
{
switch (command)
{
case "fensay":
Console.WriteLine(EasterEggs.FenSay);
break;
case "version":
Console.WriteLine(OSConsts.VersionGit);
break;
case "reboot":
var rbq = ReadLine($"Are you sure you want to {command}? 1) Yes 2) No");
switch (rbq)
{
case "1":
case "yes":
Sys.Power.Reboot();
break;
case "2":
case "no":
break;
}
break;
case "shutdown":
var shq = ReadLine($"Are you sure you want to {command}? 1) Yes 2) No");
switch (shq)
{
case "1":
case "yes":
Sys.Power.Shutdown();
break;
case "2":
case "no":
break;
}
break;
case "ls":
var dirs = TomFS.ListDirectories(command.Remove(0, 2));
Console.WriteLine(dirs);
break;
}
}
/// <summary>
/// Same as Console.ReadLine() but adds a shell command symbol
/// before the input.
/// </summary>
/// <returns>user's input</returns>
public static string ReadLine()
{
Console.Write(SYMBOL);
var readl = Console.ReadLine();
Commands(readl);
return readl;
}
/// <summary>
/// Provides a message to the user above the shell command symbol.
/// </summary>
/// <param name="message"></param>
/// <returns>user's input</returns>
public static string ReadLine(string message)
{
Console.WriteLine(message);
Console.Write(SYMBOL);
var readl = Console.ReadLine();
Commands(readl);
return readl;
}
}
}

View file

@ -37,6 +37,7 @@
<ItemGroup>
<ProjectReference Include="..\Tomas.Common\Tomas.Common.csproj" />
<ProjectReference Include="..\Tomas.Interface\Tomas.Interface.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,19 @@
namespace Tomas.Terminal
{
class Program
{
static void Main()
{
while (true)
{
var shell = new Shell();
var command = shell.ReadLine;
TermConsts.Programs.TryGetValue(command, out var program);
var isRun = program.Start();
if (isRun) continue;
break;
}
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using Tomas.Common;
using Tomas.Interface.Shell;
namespace Tomas.Terminal.Programs
{
public class About : IProgram
{
public bool Start()
{
Console.WriteLine($"{ComConsts.NAME} v{ComConsts.VersionGit}{Environment.NewLine}");
Console.WriteLine("Commands:");
var progs = TermConsts.Programs;
foreach (var commands in progs.Keys)
Console.WriteLine(commands);
return true;
}
}
}

View file

@ -0,0 +1,20 @@
using System;
using Tomas.Interface.Shell;
namespace Tomas.Terminal
{
public class Shell : IShell
{
const char SYMBOL = '$';
public string ReadLine
{
get
{
Console.Write(SYMBOL);
var readl = Console.ReadLine();
return readl;
}
}
}
}

View file

@ -0,0 +1,17 @@
using System.Collections.Generic;
using Tomas.Interface.Shell;
using Tomas.Kernel.Programs;
using Tomas.Terminal.Programs;
namespace Tomas.Terminal
{
public struct TermConsts
{
public static Dictionary<string, IProgram> Programs => new Dictionary<string, IProgram>()
{
{"about", new About()},
{"fensay", new FenSay()},
{"clear", new Clear()}
};
}
}

View file

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>8</LangVersion>
<Nullable>warnings</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Tomas.Common\Tomas.Common.csproj" />
<ProjectReference Include="..\Tomas.Interface\Tomas.Interface.csproj" />
</ItemGroup>
</Project>