Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V3 #42

Draft
wants to merge 41 commits into
base: master
Choose a base branch
from
Draft

V3 #42

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
96414f4
Execution logic modifications
csmir Jan 19, 2024
88449a1
Update package inclusions & versions
csmir Jan 22, 2024
5a2cb04
Logic implementations, refactors, finalize design
csmir Jan 24, 2024
7257843
Improve typereader gen, value duplicate checks, throwhelpers
csmir Jan 25, 2024
f0fa0bd
Simple cleanup
csmir Jan 25, 2024
2b63557
Attention to namespaces, naming convention system, parsing start
csmir Jan 25, 2024
c415fcb
new ignore
csmir Jan 25, 2024
0ed35f3
Introduce functional parsing, stringgen
csmir Jan 27, 2024
ed71ed8
Upgrades to hosting extensions, logging support
csmir Jan 27, 2024
f658c68
Improvements to asynchronous execution approach
csmir Jan 29, 2024
b513d06
Update csproj's, change helpers design
csmir Jan 29, 2024
6a67db6
Improve result handling & configuration
csmir Jan 30, 2024
4e43d0e
Finalize configuration rework
csmir Jan 30, 2024
c6da199
Analyzers yippee
csmir Jan 30, 2024
a50c42e
Document configuration
csmir Jan 30, 2024
16708a8
lazy's, manager XML
csmir Jan 30, 2024
c35e6f1
Exception & helper XML, small renames & clearing unused
csmir Jan 30, 2024
322ca28
Results, module XML
csmir Jan 30, 2024
b2a7c42
Context xml
csmir Jan 31, 2024
2aed732
Attributes, context, asyncapproach XML
csmir Feb 2, 2024
5ccd519
Result, exception, collection, helpers XML
csmir Feb 2, 2024
b0f5152
Parser, precondition XML
csmir Feb 2, 2024
16fe641
TypeReader -> TypeConverter + new xml
csmir Feb 2, 2024
ba011b4
Finalize core XML
csmir Feb 2, 2024
bb9fc56
Spelling :nerd:
csmir Feb 2, 2024
35661ff
Clear unnecessary projects
csmir Feb 2, 2024
1c75bb3
Remove TShock sample
csmir Feb 2, 2024
c597b4c
Remove spectre, tshock roots
csmir Feb 2, 2024
eb2be1e
Wiki push
csmir Feb 2, 2024
caa2b84
Move /wiki to /docs
csmir Feb 2, 2024
29691e1
Simplify naming in tree
csmir Feb 2, 2024
9f2208b
Niche bug fixes
csmir Feb 2, 2024
3e72e63
Update README.md
csmir Feb 3, 2024
41f5025
XML finalization
csmir Feb 3, 2024
b2fc24c
Merge branch 'v3' of https://github.com/Rozen4334/CSF into v3
csmir Feb 3, 2024
59b3a3c
Docs 1/x
csmir Feb 3, 2024
e3cfda0
Huge work on docs, sample continuity
csmir Feb 4, 2024
d5ab5c0
Further readme change, new files
csmir Feb 4, 2024
fbcb8d2
Auto-scope service layer logic
csmir Feb 5, 2024
8d49099
EOP(?) disposer logic
csmir Feb 5, 2024
23267b8
Document configuration, dependency injection
csmir Feb 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,5 @@ MigrationBackup/
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
/Visual Studio 2022/Visualizers
34 changes: 8 additions & 26 deletions CSF.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSF.Core", "src\CSF.Core\CS
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSF.Tests.Console", "src\CSF.Tests.Console\CSF.Tests.Console.csproj", "{A70E8C5D-0209-433B-9AB7-9C05C0DA04E3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSF.Tests.TShock", "src\CSF.Tests.TShock\CSF.Tests.TShock.csproj", "{10058F39-52CB-44D6-AD8B-597F34B50B45}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSF.Samples.Console", "examples\CSF.Samples.Console\CSF.Samples.Console.csproj", "{19F7A07B-7349-4AA5-A9CC-58F1002DFED6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{DA6771C2-541A-46E0-AF1A-B4256FF4CB5E}"
Expand All @@ -21,7 +19,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSF.Tests.Hosting", "src\CS
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSF.Samples.Hosting", "examples\CSF.Samples.Hosting\CSF.Samples.Hosting.csproj", "{D99DCD40-8A42-42F5-8385-88EDF87057CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSF.Tests.Benchmarks", "src\CSF.Tests.Benchmarks\CSF.Tests.Benchmarks.csproj", "{0A3A4351-DD9A-4E25-9CA7-8CFFA5A3EE7B}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{7EB2ED0C-26A8-4F51-81CD-A0AB13B739C9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSF.Benchmarks.Parsing", "src\CSF.Benchmarks.Parsing\CSF.Benchmarks.Parsing.csproj", "{6ABD1982-481C-4006-B830-997837E2B24D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -37,26 +37,10 @@ Global
{A70E8C5D-0209-433B-9AB7-9C05C0DA04E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A70E8C5D-0209-433B-9AB7-9C05C0DA04E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A70E8C5D-0209-433B-9AB7-9C05C0DA04E3}.Release|Any CPU.Build.0 = Release|Any CPU
{10058F39-52CB-44D6-AD8B-597F34B50B45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{10058F39-52CB-44D6-AD8B-597F34B50B45}.Debug|Any CPU.Build.0 = Debug|Any CPU
{10058F39-52CB-44D6-AD8B-597F34B50B45}.Release|Any CPU.ActiveCfg = Release|Any CPU
{10058F39-52CB-44D6-AD8B-597F34B50B45}.Release|Any CPU.Build.0 = Release|Any CPU
{B868198E-67C3-4AFB-8D19-48034E1156FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B868198E-67C3-4AFB-8D19-48034E1156FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B868198E-67C3-4AFB-8D19-48034E1156FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B868198E-67C3-4AFB-8D19-48034E1156FC}.Release|Any CPU.Build.0 = Release|Any CPU
{19F7A07B-7349-4AA5-A9CC-58F1002DFED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19F7A07B-7349-4AA5-A9CC-58F1002DFED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19F7A07B-7349-4AA5-A9CC-58F1002DFED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19F7A07B-7349-4AA5-A9CC-58F1002DFED6}.Release|Any CPU.Build.0 = Release|Any CPU
{CA734A9C-2E3A-4E61-8CD0-0476AD88F94C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA734A9C-2E3A-4E61-8CD0-0476AD88F94C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA734A9C-2E3A-4E61-8CD0-0476AD88F94C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA734A9C-2E3A-4E61-8CD0-0476AD88F94C}.Release|Any CPU.Build.0 = Release|Any CPU
{924925E1-0AE5-4B5A-B736-EB09EF5BDB60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{924925E1-0AE5-4B5A-B736-EB09EF5BDB60}.Debug|Any CPU.Build.0 = Debug|Any CPU
{924925E1-0AE5-4B5A-B736-EB09EF5BDB60}.Release|Any CPU.ActiveCfg = Release|Any CPU
{924925E1-0AE5-4B5A-B736-EB09EF5BDB60}.Release|Any CPU.Build.0 = Release|Any CPU
{BD637AD8-B1B6-4C4E-9C28-FEF0B2824B20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD637AD8-B1B6-4C4E-9C28-FEF0B2824B20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD637AD8-B1B6-4C4E-9C28-FEF0B2824B20}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -69,22 +53,20 @@ Global
{D99DCD40-8A42-42F5-8385-88EDF87057CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D99DCD40-8A42-42F5-8385-88EDF87057CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D99DCD40-8A42-42F5-8385-88EDF87057CC}.Release|Any CPU.Build.0 = Release|Any CPU
{0A3A4351-DD9A-4E25-9CA7-8CFFA5A3EE7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A3A4351-DD9A-4E25-9CA7-8CFFA5A3EE7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A3A4351-DD9A-4E25-9CA7-8CFFA5A3EE7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A3A4351-DD9A-4E25-9CA7-8CFFA5A3EE7B}.Release|Any CPU.Build.0 = Release|Any CPU
{6ABD1982-481C-4006-B830-997837E2B24D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6ABD1982-481C-4006-B830-997837E2B24D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6ABD1982-481C-4006-B830-997837E2B24D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6ABD1982-481C-4006-B830-997837E2B24D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A70E8C5D-0209-433B-9AB7-9C05C0DA04E3} = {DA6771C2-541A-46E0-AF1A-B4256FF4CB5E}
{10058F39-52CB-44D6-AD8B-597F34B50B45} = {DA6771C2-541A-46E0-AF1A-B4256FF4CB5E}
{B868198E-67C3-4AFB-8D19-48034E1156FC} = {01CCF11A-2D95-44C9-81EA-EE7D6A36FE7A}
{19F7A07B-7349-4AA5-A9CC-58F1002DFED6} = {01CCF11A-2D95-44C9-81EA-EE7D6A36FE7A}
{DC2807A7-F45F-422F-A9CB-75E1CC0A9978} = {DA6771C2-541A-46E0-AF1A-B4256FF4CB5E}
{D99DCD40-8A42-42F5-8385-88EDF87057CC} = {01CCF11A-2D95-44C9-81EA-EE7D6A36FE7A}
{0A3A4351-DD9A-4E25-9CA7-8CFFA5A3EE7B} = {DA6771C2-541A-46E0-AF1A-B4256FF4CB5E}
{6ABD1982-481C-4006-B830-997837E2B24D} = {7EB2ED0C-26A8-4F51-81CD-A0AB13B739C9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C9CE5996-C6A9-442C-8808-490D2FA61458}
Expand Down
161 changes: 149 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,152 @@
<p align="center">
<a href="https://github.com/csmir/CSF.NET/wiki">
<img src="https://user-images.githubusercontent.com/68127614/199816747-eadf3197-8be7-460a-879a-ae5ad2a903af.png" alt="logo">
</a>
</p>
![csfbanner_lighttrans_outline](https://github.com/csmir/CSF.NET/assets/68127614/7255e535-41b6-431c-87f2-2d9aa18ef6f9)

<p align="center">
<img alt="buildstatus" src="https://img.shields.io/github/actions/workflow/status/csmir/CSF.NET/dotnet.yml?branch=master&style=for-the-badge">
<a href="https://nuget.org/packages/CSF.NET"><img alt="download" src="https://img.shields.io/static/v1?style=for-the-badge&message=download%20on%20nuget&color=004880&logo=NuGet&logoColor=FFFFFF&label="></a>
<a href="https://discord.gg/T7hCvShAx5"><img alt="help" src="https://img.shields.io/discord/1092510256384450652?style=for-the-badge"></a>
</p>
# CSF.NET - Command Standardization Framework for .NET

# ⚒️ CSF.NET - The Command Standardization Framework for .NET
CSF is an attribute based framework that makes creating and processing **text based commands** easy for any platform.
It implements a modular, easy to implement pipeline for registering and executing commands, as well as a wide range of customization options to make development on different platforms as easy as possible.

Welcome to the new era of commands! CSF aims to reduce your boilerplate while keeping customizability high. It is intuitive, developer friendly and scalable, all with performance in mind. For more about writing code with this framework, visit the [documentation](https://github.com/csmir/CSF.NET/wiki)!
- [Features](#features)
- [Additional Packages](#additional-packages)
- [Getting Started](#getting-started)

## Features

#### Type Conversion

For raw input, automated conversion to fit command signature is supported by `TypeConverter`'s.
`ValueType`, `Enum` and nullable variant types are automatically parsed by the framework and populate commands as below:

```cs
...
[Command("test")]
public void Test(int param1, DateTime param2)
{
Console.WriteLine("{0}, {1}", param1, param2);
}
...
```
- This will automatically parse `int` by using the default `int.TryParse` implementation, and will do the same for the `DateTime`.

Outside of this, implementing and adding your own `TypeConverter`'s is also supported to handle command signatures with normally unsupported types.

> See feature [documentation](https://github.com/csmir/CSF.NET/wiki/Type-Conversion) for more.

#### Preconditions

Implementing `PreconditionAttribute` creates a new evaluation to add in the set of attributes defined above command definitions.
When a command is attempted to be executed, it will walk through every precondition present and abort execution if any of them fail.

```cs
...
[CustomPrecondition]
[Command("test")]
public async Task Test()
{

}
...
```

> See feature [documentation](https://github.com/csmir/CSF.NET/wiki/Preconditions) for more.

#### Dependency Injection

You can provide an `IServiceProvider` at execution to inject modules with dependencies, in accordance to the conventions `Microsoft.Extensions.DependencyInjection` follows.
The `IServiceProvider` has a number of extensions that are suggested to be used when writing your codebase with CSF.
These extensions serve you and the program, reducing boilerplate in the application setup.

```cs
...
var services = new ServiceCollection()
.ConfigureCommands(configuration =>
{
configuration.WithAssemblies(Assembly.GetEntryAssembly());
});
...
```

> See feature [documentation](https://github.com/csmir/CSF.NET/wiki/Dependency-Injection) for more.

#### Informative Results

CSF.NET will return results for running commands through a `ResultResolver`.
This resolver has a default implementation that can be configured through the `CommandConfiguration`

```cs
...
configuration.ConfigureResultAction(async (context, result, services) =>
{
if (result.Success)
{
await Task.CompletedTask;
}
else
{
Console.WriteLine(result.Exception);
}
});
...
```

> See feature [documentation](https://github.com/csmir/CSF.NET/wiki/Results) for more.

#### Customization

While already fully functional out of the box, the framework does not shy away from covering extensive applications with more specific needs, which in turn need more than the base features to function according to its developer's expectations.

Types such as `CommandContext`, `ModuleBase`, `TypeConverter`, `PreconditionAttribute` and `Parser` can all be inherited and custom ones created for environmental specifics, custom type conversion and more.

#### Reflection

The framework saves cached command data in its own reflection types.
These types, such as `CommandInfo`, `ArgumentInfo` and `ModuleInfo` store informative data about a command, its root module and any submembers.

The reflection data is accessible in various ways, most commonly in scope during type conversion & precondition evaluation.

## Additional Packages

CSF is not without its own dependencies, but it tries its best to keep all dependencies within a trusted atmosphere, using packages only when they outweigh self-written implementations. So far, it only depends on packages published by official channels of the .NET ecosystem.

#### Dependency Injection

Having grown into a vital part of building effective and modern applications, Dependency Injection (DI) is no less important to be carried along in the equally modern CSF.
It integrates this feature deeply into its architecture and depends on it to function from the ground up.

For applications to function with `CSF.Core`, it is necessary to install DI functionality through Microsoft's publicized package(s):

```xml
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="" />
```
> This and other required packages can also be installed through the Package Manager, .NET CLI or from within your IDE.

#### Hosting

Carrying out further support within the .NET ecosystem, CSF also introduces a hosting package for deploying apps with the .NET generic host.

For applications to function with `CSF.Hosting`, it is necessary to install the hosting package that it also implements, also publicized by Microsoft itself:

```xml
<PackageReference Include="Microsoft.Extensions.Hosting" Version="" />
```

> The hosting extensions package publicized by Microsoft implements the packages necessary for the core component of CSF, and does not expect to have its dependencies implemented alongside it.

*For each of these packages, the minimum version is determined by CSF itself, usually being the latest or equal to the target framework upon which it was released. It is suggested to choose the latest version at time of installation.*

## Getting Started

There are various resources available in order to get started with CSF. Below, you can find samples and directions to the quick guide.

#### Quick Guide

You can find the quick guide [here](https://github.com/csmir/CSF.NET/wiki/Quick-Guide).
This guide introduces you to the basics of defining modules, commands, and how to run them.

#### Samples

Samples are available to learn how to implement CSF in your own programs.

- [CSF.Samples.Console](https://github.com/csmir/CSF.NET/tree/master/examples/CSF.Samples.Console)
- Shows how to implement CSF on a basic console application.
- [CSF.Samples.Hosting](https://github.com/csmir/CSF.NET/tree/master/examples/CSF.Samples.Console)
- Shows how to implement CSF on a hosted console application.
74 changes: 74 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
The configuration of command registration and execution can be overwhelming if you are unfamiliar with the various options exposed. This chapter introduces the various options and elaborates the functionality covered within.

## The Command Configuration

`CommandConfiguration` serves as the base class for handling configuration options for the `CommandManager` and how it runs the execution pipeline.
It exposes a fair amount of options, each useful in different situations.

### Assemblies

Known assemblies are a core component to how CSF functions, iterating through each type defined per-assembly to register commands and writing them to the `IReadOnlySet` exposed in `CommandManager`. This is the collection used to find, match and execute commands.

By default, `Assemblies` is populated by `Assembly.GetEntryAssembly`, which serves as the entry-point executable to run the framework with.

The `CommandConfiguration` exposes an array that is accessible by the following methods:

#### `TryAddAssembly`

This method will attempt to add an `Assembly` to `Assemblies`, returning if the assembly was already added.

#### `AddAssembly`

This method will attempt to add an `Assembly` to `Assemblies`, throwing an exception if the assembly was already added.

#### `WithAssemblies`

This method will replace all current assemblies with `params Assembly[]` as passed into the method. Duplicates are automatically removed through calling `IEnumerable.Union`.

### Type Converters (Converters)

In many cases, there is need for custom `TypeConverter` implementations to convert types that CSF does not already convert for you. These all need to be registered here, in the same way as `Assemblies`. The overloads for adding new type converters is the same as the prior, with no exceptions.

### Result Resolver (ResultResolver)

Results can be handled in an elaborate many ways, as documented [[here|Results]]. This configuration option allows you to set a custom resolver or redefine the base implementation with `ConfigureResultAction`.

### Async Approach (AsyncApproach)

The `AsyncApproach` option defines how commands are ran. There are two options aside from `Default`, which is set to `Await`.

#### Await

This is the default setting and tells the pipeline to finish executing before returning control to the caller.
This ensures that the execution will fully finish executing, whether it failed or not, before allowing another to be executed.

#### Discard

Instead of waiting for the full execution before returning control, the execution will return immediately after the entrypoint is called, slipping thread for the rest of execution.
When more than one input source is expected to be handled, this is generally the advised method of execution.

Changing to this setting, the following should be checked for thread-safety:

- Services, specifically those created as singleton or scoped to anything but a single command.
- Implementations of `TypeConverter`, `TypeConverter{T}` and `PreconditionAttribute`.
- Generic collections and objects with shared access.

> To ensure thread safety in any of the above situations, it is important to know what this actually means.
> For more information, consider reading [this article](https://learn.microsoft.com/en-us/dotnet/standard/threading/managed-threading-best-practices).

### Scope Approach (ScopeApproach)

#### OnlyAsync

This option forces the `IServiceProvider` to use `CreateAsyncScope` to generate new scopes. This structure wraps around `IServiceScope` to carry the ability to handle `IAsyncDisposable` types. This is usually unnecessary, unless if `AsyncApproach` is set to `Discard` but no `IAsyncDisposable` services exist.

#### OnlySync

Here, the option forces the `IServiceProvider` to use `CreateScope`. If no settings are changed in the `CommandConfiguration`, this is the default approach. It is unusual for smaller applications to have scopes that implement `IAsyncDisposable` pattern. Though, if this is the case, this setting must be changed.

#### ByAsyncApproach

Here, `AsyncApproach` determines the value (one of the above).

- When set to `Discard`, it will follow the logic as set in `OnlyAsync`.
- When set to `Await`, it will follow `OnlySync` logic.
Empty file added docs/Customization.md
Empty file.
Loading