initial commit

This commit is contained in:
Booklordofthedings 2024-05-12 00:11:43 +02:00
commit 29bb03ffe1
6 changed files with 297 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build/
recovery/
BeefSpace_User.toml

6
BeefProj.toml Normal file
View file

@ -0,0 +1,6 @@
FileVersion = 1
[Project]
Name = "LybCL"
TargetType = "BeefLib"
StartupObject = "LybCL.Program"

2
BeefSpace.toml Normal file
View file

@ -0,0 +1,2 @@
FileVersion = 1
Projects = {LybCL = {Path = "."}}

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Jannis vH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

11
README.md Normal file
View file

@ -0,0 +1,11 @@
## LybCL
A simpleish commandline parsing library with automatic routing functionality
### Installation
Clone the repository and add it to your beef workspace as you usually would.
This libary is also single file, so you may also just copy the file directly into your project if necessary
### Documentation
The .bf sourcefile contains all information neccessary to use the library.
You basically just create a new LybCl object by inserting the args[] and then call function on that object.
The CMDRouter attribute can be used for the automatic routing functionality, however you will need to ensure the type is refelcted.

254
src/LybCl.bf Normal file
View file

@ -0,0 +1,254 @@
/*
[LybCL]
Lyb Commandline library/tool
Parses the String[] that a program starts with and allows the user to use the arguments easier
Made by Booklordofthedings
Usage:
Everything starts by creating a commandline object and by passing in the String[]
LybCl handles everything else and you only need to make sure to correctly dispose of it.
GetArgs(int) returns the value of the args by index, if it exists. Otherwise it returns an empty stringview
GetCommand() returns the command thats to be executed: ./Programname.exe {command} -parameters
HasFlag(params StringView) checks wether a given flag is set as a value. returns a boolean
GetParameter(params StringView) checks the value of a given parameter. Returns an empty string if its not found.
RouteReturnCode{get;set} allows you to set the return value of the Route() call. only use inside one of the marked methods
Route() Does reflection and calls functions that match the given route
-Use CMDRouterAttriíbute on a static function to mark it as a routeable functions
-The first attribute needs to be a LybCl type
-Any other parameter needs to have be nullable (Type?) and have a Parse(StringView) function that returns a Result<T>
-the route is made out of literals divided by spaces, while the required variables are to be included inside of curly brackets
"generate new {a}" <- this route executes, if the user calls Program.exe generate new {any}. any being any value that will be parsed and inserted into the route
Parameter design:
[ProgramName] [Command] [Subcommand] [Parameter] [Arguments/Flags]
[ProgramName] name of the program/exectuteable to run (test.exe/git)
[Command]/[Subcommands] indicates the route or specific functionality to be executed. may be only one command (install) or multiple (get install) or any other lenght
[Parameter] variable values that a command needs, like a filepath or url
[Arguments/Flags] contain additional information that is not required but may change how the command executes. flags are only boolean while arguments have to contain
an example call would be something like this
./Program.exe repository generate "https://github.com/booklordofthedings/repo" --verbose --logfile log.txt
some programs support arguments between the program name and command, however this tool does not
if you want to do global settings that apply to every command you can simply put them at the end of the command
and just read them in before doing any processing or routing, to set them
*/
namespace LybCL;
using System;
using System.Reflection;
using System.Collections;
class LybCl
{
private String[] _args;
private Dictionary<String, String> _parameters = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
private CMDRouterAttribute _attributeInfo;
public this(String[] pArgs)
{
_args = pArgs;
for(int i < _args.Count)
{
if(!_args[i].StartsWith('-'))
continue;
int endIndex = -1;
for(int j < _args[i].Length)
{
if(_args[i][j] != '-')
{
endIndex = j;
break;
}
}
if(endIndex < 0)
continue;
StringView name = .(_args[i],endIndex);
if(!(i+1 < _args.Count) || _args[i+1].StartsWith('-'))
{
_parameters.Add(new .(name), new .(""));
continue;
}
_parameters.Add(new .(name), new .(_args[i+1]));
i++;
}
}
//Allows users to set what the call for route will return
public int RouteReturnCode { get; set; } = 0;
public int Count
{
public get {
return _args.Count;
}
}
///Get the raw argument at the given index or an empty string
public StringView GetArgs(int pIndex)
{
if(pIndex < 0 || !(pIndex < _args.Count))
return "";
else
return _args[pIndex];
}
///Get the command or an empty string
public StringView GetCommand()
{
if(_args.Count < 1)
return "";
else if(!_args[0].StartsWith('-'))
return _args[0];
else
return "";
}
///Get wether a specific flag is set, allows aliases
public bool HasFlag(params Span<StringView> pArgs)
{
for(var arg in pArgs)
{
if(!_parameters.ContainsKey(scope .(arg)))
continue;
if(_parameters[scope .(arg)] == "")
return true;
}
return false;
}
///Gets the value of a argument and returns empty if not found
public StringView GetParameter(params Span<StringView> pArgs)
{
for(var arg in pArgs)
{
if(!_parameters.ContainsKey(scope .(arg)))
continue;
return _parameters[scope .(arg)];
}
return "";
}
///Process all of the reflected and routed methods and tries to find one that matches, then it calls it
public void Route()
{
for(let type in Type.Types)
{
m: for(let method in type.GetMethods())
{
let attributeInfo = method.GetCustomAttribute<CMDRouterAttribute>();
if(attributeInfo case .Err)
continue;
_attributeInfo = attributeInfo.Value;
//Ensure that the route works
var routingList = scope List<StringView>();
attributeInfo.Value.GetRouteList(routingList);
r: for(int i < routingList.Count)
{
if(!(i < _args.Count))
continue m;
if(routingList[i].StartsWith('{') && routingList[i].EndsWith('}'))
continue r;
if(routingList[i] != _args[i])
continue m;
}
Object[] args = scope Object[method.ParamCount];
for(int i = 0; i < method.ParamCount; i++)
{
if(i == 0)
{
args[i] = this;
continue;
}
for(var fd in method.GetParamType(i).GetMethods())
{
Console.WriteLine(fd.Name);
}
args[i] = null;
}
var res = method.Invoke(null, params args);
res.Dispose();
}
}
}
public StringView GetValue(StringView pVal)
{
var route = scope List<StringView>();
_attributeInfo.GetRouteList(route);
for(int i < route.Count)
{
if(route[i] == scope $"\{{pVal}\}")
{
if(i < _args.Count)
return _args[i];
}
}
return "";
}
}
[AttributeUsage(.Method, .AlwaysIncludeTarget | .ReflectAttribute, ReflectUser=.All), AlwaysInclude]
struct CMDRouterAttribute : Attribute, IOnMethodInit
{
private String _route;
public this(String pRoute)
{
_route = pRoute;
}
public void GetRouteList(List<StringView> pBuffer)
{
var seperationChars = scope char8[](' ','\t');
var enumerator = _route.Split(seperationChars,.RemoveEmptyEntries);
for(let entry in enumerator)
pBuffer.Add(entry);
}
[Comptime]
public void OnMethodInit(MethodInfo methodInfo, Self* prev)
{
String toAppend = scope .();
for(int i = 1; i < methodInfo.ParamCount; i++)
{
//Remove nullable
var t = methodInfo.GetParamType(i) as SpecializedGenericType;
toAppend.Append(scope $"""
var {methodInfo.GetParamName(i)};
let parseResult{methodInfo.GetParamName(i)} = {t.GetGenericArg(0).GetFullName(.. scope .())}.Parse(cl.GetValue("{methodInfo.GetParamName(i)}"));
if(parseResult{methodInfo.GetParamName(i)} case .Err)
return;
{methodInfo.GetParamName(i)} = ({methodInfo.GetParamType(i)})parseResult{methodInfo.GetParamName(i)}.Value;
""");
}
Compiler.EmitMethodEntry(methodInfo, toAppend);
}
}
/*
Here are the types that dont have a
*/
namespace System
{
extension StringView
{
public static Result<StringView> Parse(StringView pInput)
{
return .Ok(pInput);
}
}
}