Merge branch 'main' of https://git.innov.energy/Innovenergy/git_trunk
This commit is contained in:
commit
45ed859826
|
@ -81,7 +81,57 @@ public class Controller
|
||||||
|
|
||||||
return installation;
|
return installation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Returns<Installation>]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetUsersWithAccessToInstallation)}")]
|
||||||
|
public Object GetUsersWithAccessToInstallation(Int64 id)
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
if (user == null)
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var installation = Db.GetInstallationById(id);
|
||||||
|
|
||||||
|
if (installation is null || !user.HasAccessTo(installation))
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var usersWithInheritedAccess = installation
|
||||||
|
.Ancestors()
|
||||||
|
.SelectMany(f => f.UsersWithDirectAccess()
|
||||||
|
.Where(u => u.IsDescendantOf(user))
|
||||||
|
.Select(u => new { folderId = f.Id, user = u }))
|
||||||
|
.OfType<Object>();
|
||||||
|
|
||||||
|
var usersWithDirectAccess = installation.UsersWithDirectAccess()
|
||||||
|
.Where(u => u.IsDescendantOf(user))
|
||||||
|
.Select(u => new { installationId = installation.Id, user = u })
|
||||||
|
.OfType<Object>();
|
||||||
|
|
||||||
|
return usersWithInheritedAccess.Concat(usersWithDirectAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Returns<Installation>]
|
||||||
|
[Returns(Unauthorized)]
|
||||||
|
[HttpGet($"{nameof(GetUsersWithAccessToFolder)}")]
|
||||||
|
public Object GetUsersWithAccessToFolder(Int64 id)
|
||||||
|
{
|
||||||
|
var user = GetSession()?.User;
|
||||||
|
if (user == null)
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
var folder = Db.GetFolderById(id);
|
||||||
|
|
||||||
|
if (folder is null || !user.HasAccessTo(folder))
|
||||||
|
return _Unauthorized;
|
||||||
|
|
||||||
|
return folder
|
||||||
|
.Ancestors()
|
||||||
|
.Append(folder)
|
||||||
|
.SelectMany(f => f.UsersWithDirectAccess()
|
||||||
|
.Where(u => u.IsDescendantOf(user))
|
||||||
|
.Select(u => new { folderId = f.Id, user = u }));
|
||||||
|
}
|
||||||
|
|
||||||
[Returns<Folder>]
|
[Returns<Folder>]
|
||||||
[Returns(Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
|
|
|
@ -15,5 +15,5 @@ public class Installation : TreeNode
|
||||||
|
|
||||||
public String S3Bucket { get; set; } = "";
|
public String S3Bucket { get; set; } = "";
|
||||||
public String S3Url { get; set; } = "";
|
public String S3Url { get; set; } = "";
|
||||||
|
|
||||||
}
|
}
|
|
@ -8,12 +8,14 @@ public static class CredentialsMethods
|
||||||
{
|
{
|
||||||
public static Session? Login(this Credentials credentials)
|
public static Session? Login(this Credentials credentials)
|
||||||
{
|
{
|
||||||
if (credentials.Username.IsNullOrEmpty() || credentials.Password.IsNullOrEmpty())
|
var (username, password) = credentials;
|
||||||
|
|
||||||
|
if (username.IsNullOrEmpty() || password.IsNullOrEmpty())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var user = Db.GetUserByEmail(credentials.Username);
|
var user = Db.GetUserByEmail(username);
|
||||||
|
|
||||||
if (user is null || !user.VerifyPassword(credentials.Password))
|
if (user is null || !user.VerifyPassword(password))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var session = new Session(user);
|
var session = new Session(user);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections;
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
@ -5,6 +6,25 @@ namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
public static class FolderMethods
|
public static class FolderMethods
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithAccess(this Folder folder)
|
||||||
|
{
|
||||||
|
return UsersWithDirectAccess(folder).Concat(UsersWithInheritedAccess(folder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithDirectAccess(this Folder folder)
|
||||||
|
{
|
||||||
|
return Db.FolderAccess
|
||||||
|
.Where(access => access.FolderId == folder.Id)
|
||||||
|
.Select(access => Db.GetUserById(access.UserId))
|
||||||
|
.NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithInheritedAccess(this Folder folder)
|
||||||
|
{
|
||||||
|
return folder.Ancestors().SelectMany(f => f.UsersWithDirectAccess()).NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<Folder> ChildFolders(this Folder parent)
|
public static IEnumerable<Folder> ChildFolders(this Folder parent)
|
||||||
{
|
{
|
||||||
return Db
|
return Db
|
||||||
|
@ -21,7 +41,9 @@ public static class FolderMethods
|
||||||
|
|
||||||
public static IEnumerable<Folder> DescendantFolders(this Folder parent)
|
public static IEnumerable<Folder> DescendantFolders(this Folder parent)
|
||||||
{
|
{
|
||||||
return parent.Traverse(ChildFolders);
|
return parent
|
||||||
|
.TraverseDepthFirstPreOrder(ChildFolders)
|
||||||
|
.Skip(1); // skip self
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean IsDescendantOf(this Folder folder, Folder ancestor)
|
public static Boolean IsDescendantOf(this Folder folder, Folder ancestor)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
using CliWrap.Buffered;
|
using CliWrap.Buffered;
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
@ -11,10 +12,10 @@ public static class InstallationMethods
|
||||||
{
|
{
|
||||||
await RenewS3BucketUrl(installation, TimeSpan.FromDays(1));
|
await RenewS3BucketUrl(installation, TimeSpan.FromDays(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task RenewS3BucketUrl(this Installation installation, TimeSpan validity)
|
public static async Task RenewS3BucketUrl(this Installation installation, TimeSpan validity)
|
||||||
{
|
{
|
||||||
//secret 55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU
|
const String secret = "55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU";
|
||||||
const String apiKey = "EXO44d2979c8e570eae81ead564";
|
const String apiKey = "EXO44d2979c8e570eae81ead564";
|
||||||
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
|
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||||
var cmd = Cli
|
var cmd = Cli
|
||||||
|
@ -22,16 +23,70 @@ public static class InstallationMethods
|
||||||
.WithArguments(new[]
|
.WithArguments(new[]
|
||||||
{
|
{
|
||||||
"Resources/s3cmd.py", "signurl", $"s3://{installation.Id}-{salt}", validity.TotalSeconds.ToString(), "--access_key",
|
"Resources/s3cmd.py", "signurl", $"s3://{installation.Id}-{salt}", validity.TotalSeconds.ToString(), "--access_key",
|
||||||
apiKey
|
apiKey, "--secret_key", secret
|
||||||
});
|
});
|
||||||
var x = await cmd.ExecuteBufferedAsync();
|
var x = await cmd.ExecuteBufferedAsync();
|
||||||
installation.S3Url = x.StandardOutput.Replace("\n", "").Replace(" ", "");
|
installation.S3Url = x.StandardOutput.Replace("\n", "").Replace(" ", "");
|
||||||
|
|
||||||
Console.WriteLine(installation.S3Url);
|
Db.Update(installation);
|
||||||
|
|
||||||
Db.Update(installation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task<Boolean> CreateBucket(this Installation installation)
|
||||||
|
{
|
||||||
|
//NOTE this key has all the rights, please be sure you know what you're doing
|
||||||
|
|
||||||
|
const String secret = "z8brNDUAbpktvyWZN1jMIrsQhavDgK2t4cb8GLvsxYg";
|
||||||
|
|
||||||
|
const String apiKey = "EXO277645911ee6bde3875e99ae";
|
||||||
|
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||||
|
var cmd = Cli
|
||||||
|
.Wrap("python3")
|
||||||
|
.WithArguments(new[]
|
||||||
|
{
|
||||||
|
"Resources/s3cmd.py", "mb", $"s3://{installation.Id}-{salt}", "--access_key",
|
||||||
|
apiKey, "--secret_key", secret
|
||||||
|
});
|
||||||
|
var x = await cmd.ExecuteBufferedAsync();
|
||||||
|
return x.ExitCode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<Boolean> DeleteBucket(this Installation installation)
|
||||||
|
{
|
||||||
|
//NOTE this key has all the rights, please be sure you know what you're doing
|
||||||
|
const String secret = "z8brNDUAbpktvyWZN1jMIrsQhavDgK2t4cb8GLvsxYg";
|
||||||
|
const String apiKey = "EXO277645911ee6bde3875e99ae";
|
||||||
|
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
|
||||||
|
var cmd = Cli
|
||||||
|
.Wrap("python3")
|
||||||
|
.WithArguments(new[]
|
||||||
|
{
|
||||||
|
"Resources/s3cmd.py", "rb", $"s3://{installation.Id}-{salt}", "--access_key",
|
||||||
|
apiKey
|
||||||
|
});
|
||||||
|
var x = await cmd.ExecuteBufferedAsync();
|
||||||
|
return x.ExitCode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithAccess(this Installation installation)
|
||||||
|
{
|
||||||
|
return UsersWithDirectAccess(installation).Concat(UsersWithInheritedAccess(installation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithDirectAccess(this Installation installation)
|
||||||
|
{
|
||||||
|
return Db.InstallationAccess
|
||||||
|
.Where(access => access.InstallationId == installation.Id)
|
||||||
|
.Select(access => Db.GetUserById(access.UserId))
|
||||||
|
.NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> UsersWithInheritedAccess(this Installation installation)
|
||||||
|
{
|
||||||
|
return installation.Ancestors().SelectMany(f => f.UsersWithDirectAccess()).NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<Folder> Ancestors(this Installation installation)
|
public static IEnumerable<Folder> Ancestors(this Installation installation)
|
||||||
{
|
{
|
||||||
var parentFolder = Parent(installation);
|
var parentFolder = Parent(installation);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using InnovEnergy.App.Backend.Relations;
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
@ -48,7 +49,8 @@ public static class SessionMethods
|
||||||
&& installation is not null
|
&& installation is not null
|
||||||
&& user.HasWriteAccess
|
&& user.HasWriteAccess
|
||||||
&& user.HasAccessTo(installation.Parent())
|
&& user.HasAccessTo(installation.Parent())
|
||||||
&& Db.Create(installation);
|
&& Db.Create(installation)
|
||||||
|
&& InstallationMethods.CreateBucket(installation).Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Update(this Session? session, Installation? installation)
|
public static Boolean Update(this Session? session, Installation? installation)
|
||||||
|
@ -90,13 +92,20 @@ public static class SessionMethods
|
||||||
public static Boolean Update(this Session? session, User? editedUser)
|
public static Boolean Update(this Session? session, User? editedUser)
|
||||||
{
|
{
|
||||||
var sessionUser = session?.User;
|
var sessionUser = session?.User;
|
||||||
|
if (editedUser == null || sessionUser == null) return false;
|
||||||
|
|
||||||
return sessionUser is not null
|
|
||||||
&& editedUser is not null
|
//Password change is only allowed for oneself
|
||||||
&& sessionUser.HasWriteAccess
|
if ( editedUser.Id != sessionUser.Id) editedUser.Password = sessionUser.Password;
|
||||||
&& sessionUser.HasAccessTo(editedUser)
|
else
|
||||||
//&& (editedUser.IsRelativeRoot() || sessionUser.HasAccessTo(editedUser.Parent())) // TODO: triple check this
|
{
|
||||||
&& Db.Update(editedUser);
|
editedUser.Password = sessionUser.SaltAndHashPassword(editedUser.Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionUser.HasWriteAccess
|
||||||
|
&& sessionUser.HasAccessTo(editedUser)
|
||||||
|
&& (editedUser.IsRelativeRoot() || sessionUser.HasAccessTo(editedUser.Parent()) || editedUser.Id == sessionUser.Id) // TODO: triple check this
|
||||||
|
&& Db.Update(editedUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Delete(this Session? session, User? userToDelete)
|
public static Boolean Delete(this Session? session, User? userToDelete)
|
||||||
|
|
|
@ -73,12 +73,14 @@ public static class UserMethods
|
||||||
|
|
||||||
public static IEnumerable<User> DescendantUsers(this User parent)
|
public static IEnumerable<User> DescendantUsers(this User parent)
|
||||||
{
|
{
|
||||||
return parent.Traverse(ChildUsers);
|
return parent
|
||||||
|
.TraverseDepthFirstPreOrder(ChildUsers)
|
||||||
|
.Skip(1); // skip self
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean IsDescendantOf(this User user, User ancestor)
|
public static Boolean IsDescendantOf(this User user, User ancestor)
|
||||||
{
|
{
|
||||||
if (user.Id == ancestor.Id) return true;
|
// if (user.Id == ancestor.Id) return true;
|
||||||
return user
|
return user
|
||||||
.Ancestors()
|
.Ancestors()
|
||||||
.Any(u => u.Id == ancestor.Id);
|
.Any(u => u.Id == ancestor.Id);
|
||||||
|
|
|
@ -40,16 +40,12 @@ public static partial class Db
|
||||||
public static Boolean Update(User user)
|
public static Boolean Update(User user)
|
||||||
{
|
{
|
||||||
var originalUser = GetUserById(user.Id);
|
var originalUser = GetUserById(user.Id);
|
||||||
|
|
||||||
//Todo change password backend
|
|
||||||
user.Password = originalUser.Password;
|
|
||||||
|
|
||||||
return originalUser is not null
|
return originalUser is not null
|
||||||
&& user.Id == originalUser.Id // these columns must not be modified!
|
&& user.Id == originalUser.Id // these columns must not be modified!
|
||||||
&& user.ParentId == originalUser.ParentId
|
&& user.ParentId == originalUser.ParentId
|
||||||
&& user.Email == originalUser.Email
|
&& user.Email == originalUser.Email
|
||||||
&& user.Password == originalUser.Password
|
&& Connection.InsertOrReplace(user) > 0;
|
||||||
&& Connection.InsertOrReplace(user) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Update(this Session session)
|
public static Boolean Update(this Session session)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Reactive.Linq;
|
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
@ -8,7 +7,6 @@ public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(String[] args)
|
public static void Main(String[] args)
|
||||||
{
|
{
|
||||||
|
|
||||||
Db.CreateFakeRelations();
|
Db.CreateFakeRelations();
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,147 @@
|
||||||
|
namespace InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
public static class TreeTraversal
|
||||||
|
{
|
||||||
|
// public static IEnumerable<T> TraverseDepthFirstPreOrder<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||||
|
// {
|
||||||
|
// var stack = new Stack<IEnumerator<T>>();
|
||||||
|
//
|
||||||
|
// var iterator = root.AsSingleEnumerator();
|
||||||
|
//
|
||||||
|
// while (true)
|
||||||
|
// {
|
||||||
|
// while (iterator.MoveNext())
|
||||||
|
// {
|
||||||
|
// yield return iterator.Current;
|
||||||
|
//
|
||||||
|
// iterator = getChildren(iterator.Current).GetEnumerator();
|
||||||
|
// stack.Push(iterator);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// iterator.Dispose();
|
||||||
|
// if (stack.Count == 0)
|
||||||
|
// yield break;
|
||||||
|
//
|
||||||
|
// iterator = stack.Pop();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static IEnumerable<T> TraverseDepthFirstPreOrder<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||||
|
{
|
||||||
|
return Traverse(root,
|
||||||
|
getChildren,
|
||||||
|
branchOpen: true,
|
||||||
|
branchClose: false,
|
||||||
|
leaf: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<T> TraverseDepthFirstPostOrder<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||||
|
{
|
||||||
|
return Traverse(root,
|
||||||
|
getChildren,
|
||||||
|
branchOpen: false,
|
||||||
|
branchClose: true,
|
||||||
|
leaf: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<T> TraverseLeaves<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||||
|
{
|
||||||
|
return Traverse(root,
|
||||||
|
getChildren,
|
||||||
|
branchOpen: false,
|
||||||
|
branchClose: true,
|
||||||
|
leaf: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static IEnumerable<T> Traverse<T>(T root,
|
||||||
|
Func<T, IEnumerable<T>> getChildren,
|
||||||
|
Boolean branchOpen,
|
||||||
|
Boolean branchClose,
|
||||||
|
Boolean leaf)
|
||||||
|
{
|
||||||
|
// the if-checks on the constant booleans are
|
||||||
|
// almost free because of modern branch predictors
|
||||||
|
|
||||||
|
var stack = new Stack<IEnumerator<T>>();
|
||||||
|
var it = root.AsSingleEnumerator();
|
||||||
|
it.MoveNext();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
//////// going down ////////
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var cit = getChildren(it.Current).GetEnumerator();
|
||||||
|
|
||||||
|
if (cit.MoveNext()) // node has children, must be a branch
|
||||||
|
{
|
||||||
|
if (branchOpen)
|
||||||
|
yield return it.Current;
|
||||||
|
|
||||||
|
stack.Push(it);
|
||||||
|
it = cit;
|
||||||
|
}
|
||||||
|
else // no children, hence a leaf
|
||||||
|
{
|
||||||
|
var node = it.Current;
|
||||||
|
|
||||||
|
if (leaf)
|
||||||
|
yield return node;
|
||||||
|
|
||||||
|
if (!it.MoveNext())
|
||||||
|
break; // no more siblings: goto parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////// going up ////////
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
it.Dispose();
|
||||||
|
if (stack.Count == 0) yield break; // we got to the bottom of the stack, were done
|
||||||
|
|
||||||
|
it = stack.Pop();
|
||||||
|
|
||||||
|
var node = it.Current;
|
||||||
|
|
||||||
|
if (branchClose)
|
||||||
|
yield return node; // we've seen all its children: close the branch
|
||||||
|
|
||||||
|
if (it.MoveNext())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IEnumerable<T> TraverseBreadthFirst<T>(this T node, Func<T, IEnumerable<T>> getChildren)
|
||||||
|
{
|
||||||
|
var queue = new Queue<IEnumerator<T>>();
|
||||||
|
|
||||||
|
var iterator = node.AsSingleEnumerator();
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
while (iterator.MoveNext())
|
||||||
|
{
|
||||||
|
yield return iterator.Current;
|
||||||
|
|
||||||
|
iterator.Current
|
||||||
|
.Apply(getChildren)
|
||||||
|
.GetEnumerator()
|
||||||
|
.Apply(queue.Enqueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Dispose();
|
||||||
|
|
||||||
|
if (queue.Count == 0)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
iterator = queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using static System.Runtime.CompilerServices.MethodImplOptions;
|
using static System.Runtime.CompilerServices.MethodImplOptions;
|
||||||
|
|
||||||
|
@ -6,6 +7,7 @@ namespace InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
public static class Utils
|
public static class Utils
|
||||||
{
|
{
|
||||||
|
public static Boolean IsNull<T>([NotNullWhen(returnValue: false)] this T t) => t is null;
|
||||||
|
|
||||||
public static IEnumerable<String> GetEnumStrings<T>(this T e) where T : Enum
|
public static IEnumerable<String> GetEnumStrings<T>(this T e) where T : Enum
|
||||||
{
|
{
|
||||||
|
@ -20,7 +22,7 @@ public static class Utils
|
||||||
[DebuggerStepThrough][MethodImpl(AggressiveInlining)]
|
[DebuggerStepThrough][MethodImpl(AggressiveInlining)]
|
||||||
public static T ConvertTo<T>(this IConvertible c) where T : IConvertible
|
public static T ConvertTo<T>(this IConvertible c) where T : IConvertible
|
||||||
{
|
{
|
||||||
var t = typeof (T);
|
var t = typeof(T);
|
||||||
|
|
||||||
var type = t.IsEnum
|
var type = t.IsEnum
|
||||||
? Enum.GetUnderlyingType(t)
|
? Enum.GetUnderlyingType(t)
|
||||||
|
@ -50,13 +52,13 @@ public static class Utils
|
||||||
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||||
public static R Apply<T, R>(this T t, Func<T, R> f) => f(t);
|
public static R Apply<T, R>(this T t, Func<T, R> f) => f(t);
|
||||||
|
|
||||||
[DebuggerStepThrough]
|
|
||||||
[MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||||
public static R Apply<T1, T2, R>(this (T1 p1, T2 p2) t, Func<T1, T2, R> f) => f(t.p1, t.p2);
|
public static R Apply<T1, T2, R>(this (T1 p1, T2 p2) t, Func<T1, T2, R> f) => f(t.p1, t.p2);
|
||||||
|
|
||||||
[DebuggerStepThrough]
|
|
||||||
[MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||||
public static R Apply<T1, T2, T3, R>(this (T1 p1, T2 p2, T3 p3) t, Func<T1, T2, T3, R> f) => f(t.p1, t.p2, t.p3);
|
public static R Apply<T1, T2, T3, R>(this (T1 p1, T2 p2, T3 p3) t, Func<T1, T2, T3, R> f) => f(t.p1, t.p2, t.p3);
|
||||||
|
|
||||||
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||||
public static R ApplyOrDefault<T, R>(this T t, Func<T, R> f, R @default)
|
public static R ApplyOrDefault<T, R>(this T t, Func<T, R> f, R @default)
|
||||||
|
@ -79,7 +81,7 @@ public static class Utils
|
||||||
? res
|
? res
|
||||||
: res + length;
|
: res + length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Decimal Modulo(this Decimal dividend, Decimal divisor)
|
public static Decimal Modulo(this Decimal dividend, Decimal divisor)
|
||||||
{
|
{
|
||||||
var res = dividend % divisor;
|
var res = dividend % divisor;
|
||||||
|
@ -89,111 +91,39 @@ public static class Utils
|
||||||
: res + divisor;
|
: res + divisor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<T> Traverse<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
|
||||||
{
|
|
||||||
var stack = new Stack<IEnumerator<T>>();
|
|
||||||
var it = root.AsSingleEnumerator();
|
|
||||||
it.MoveNext();
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
//////// going down ////////
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var cit = getChildren(it.Current).GetEnumerator();
|
|
||||||
|
|
||||||
if (cit.MoveNext()) // node has children, must be a branch
|
|
||||||
{
|
|
||||||
yield return it.Current;
|
|
||||||
|
|
||||||
stack.Push(it);
|
|
||||||
it = cit;
|
|
||||||
}
|
|
||||||
else // no children, hence a leaf
|
|
||||||
{
|
|
||||||
var node = it.Current;
|
|
||||||
|
|
||||||
yield return node;
|
|
||||||
|
|
||||||
if (!it.MoveNext())
|
|
||||||
break; // no more siblings: goto parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////// going up ////////
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
it.Dispose();
|
|
||||||
if (stack.Count == 0)
|
|
||||||
yield break; // we got to the bottom of the stack, were done
|
|
||||||
|
|
||||||
it = stack.Pop();
|
|
||||||
|
|
||||||
if (it.MoveNext())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Int32 Clamp(this Int32 value, Int32 minValue, Int32 maxValue)
|
public static Int32 Clamp(this Int32 value, Int32 minValue, Int32 maxValue)
|
||||||
{
|
{
|
||||||
var clamped = Math.Min(maxValue, value);
|
var clamped = Math.Min(maxValue, value);
|
||||||
clamped = Math.Max(minValue, clamped);
|
clamped = Math.Max(minValue, clamped);
|
||||||
|
|
||||||
return clamped;
|
return clamped;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Double Clamp(this Double value, Double minValue, Double maxValue)
|
public static Double Clamp(this Double value, Double minValue, Double maxValue)
|
||||||
{
|
{
|
||||||
var clamped = Math.Min(maxValue, value);
|
var clamped = Math.Min(maxValue, value);
|
||||||
clamped = Math.Max(minValue, clamped);
|
clamped = Math.Max(minValue, clamped);
|
||||||
|
|
||||||
return clamped;
|
return clamped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Decimal Clamp(this Decimal value, Decimal minValue, Decimal maxValue)
|
public static Decimal Clamp(this Decimal value, Decimal minValue, Decimal maxValue)
|
||||||
{
|
{
|
||||||
var clamped = Math.Min(maxValue, value);
|
var clamped = Math.Min(maxValue, value);
|
||||||
clamped = Math.Max(minValue, clamped);
|
clamped = Math.Max(minValue, clamped);
|
||||||
|
|
||||||
return clamped;
|
return clamped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static R ValueOrDefault<T, R>(this Dictionary<T, R> dict, T key, R defaultValue) where T : notnull
|
||||||
#pragma warning disable 8714
|
|
||||||
|
|
||||||
public static async Task<R> ApplyOrDefault<T, R>(this T t, Func<T, Task<R>> f, R @default)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await f(t);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return @default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static R ValueOrDefault<T, R>(this Dictionary<T, R> dict, T key, R defaultValue)
|
|
||||||
{
|
{
|
||||||
return dict.TryGetValue(key, out var value)
|
return dict.TryGetValue(key, out var value)
|
||||||
? value
|
? value
|
||||||
: defaultValue;
|
: defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Dictionary<K, V> CombineDicts<K,V>(params IEnumerable<KeyValuePair<K,V>>[] dicts)
|
|
||||||
{
|
|
||||||
return dicts.Flatten().ToDictionary(kv => kv.Key, kv => kv.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning restore 8714
|
|
||||||
|
|
||||||
|
|
||||||
public static void CopyFilesRecursively(String source, String target)
|
public static void CopyFilesRecursively(String source, String target)
|
||||||
{
|
{
|
||||||
CopyFilesRecursively(new DirectoryInfo(source), new DirectoryInfo(target));
|
CopyFilesRecursively(new DirectoryInfo(source), new DirectoryInfo(target));
|
||||||
|
@ -210,27 +140,4 @@ public static class Utils
|
||||||
|
|
||||||
|
|
||||||
public static String ExecutingProcessName => Process.GetCurrentProcess().ProcessName;
|
public static String ExecutingProcessName => Process.GetCurrentProcess().ProcessName;
|
||||||
|
|
||||||
public static IEnumerable<IEnumerable<T>> TraverseWithPath<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
|
||||||
{
|
|
||||||
var stack = new Stack<IEnumerator<T>>();
|
|
||||||
stack.Push(root.AsSingleEnumerator());
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
for (var top = stack.Peek(); top.MoveNext(); top = Push(top))
|
|
||||||
yield return stack.Select(p => p.Current);
|
|
||||||
|
|
||||||
var popped = stack.Pop();
|
|
||||||
popped.Dispose();
|
|
||||||
}
|
|
||||||
while (stack.Count > 0);
|
|
||||||
|
|
||||||
IEnumerator<T> Push(IEnumerator<T> node)
|
|
||||||
{
|
|
||||||
var top = getChildren(node.Current).GetEnumerator();
|
|
||||||
stack.Push(top);
|
|
||||||
return top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue