This commit is contained in:
2022-11-01 15:01:20 +01:00
parent e379f8b83f
commit aa61ef3ce1
33 changed files with 890 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Alma</RootNamespace>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,7 @@
namespace Alma.Command;
public interface ICommand
{
public string CommandString { get; }
public Task Run(List<string> parameters);
}

View File

@@ -0,0 +1,17 @@
namespace Alma.Configuration.Module;
public record ModuleConfiguration(string? Target, Dictionary<string, string>? Links)
{
public ModuleConfiguration Merge(ModuleConfiguration merge)
{
var mergedLinks = (Links ?? new Dictionary<string, string>())
.Concat(merge.Links ?? new Dictionary<string, string>());
return new ModuleConfiguration(
merge.Target ?? Target,
new Dictionary<string, string>(mergedLinks)
);
}
public static ModuleConfiguration Empty() =>
new(null, new Dictionary<string, string>());
}

View File

@@ -0,0 +1,5 @@
namespace Alma.Configuration.Module;
public class ModuleConfigurationRoot : Dictionary<string, ModuleConfiguration>
{
}

View File

@@ -0,0 +1,7 @@
namespace Alma.Configuration.Repository;
public interface IRepositoryConfiguration
{
public Task LoadAsync();
RepositoryConfigurationRoot Configuration { get; }
}

View File

@@ -0,0 +1,7 @@
namespace Alma.Configuration.Repository;
public record RepositoryConfigurationEntry(
string Name,
string? RepositoryPath,
string? LinkPath
);

View File

@@ -0,0 +1,3 @@
namespace Alma.Configuration.Repository;
public record RepositoryConfigurationRoot(List<RepositoryConfigurationEntry> Repositories);

View File

@@ -0,0 +1,6 @@
namespace Alma.Data;
public static class Constants
{
public static readonly string ModuleConfigFileStub = ".alma-config";
}

View File

@@ -0,0 +1,3 @@
namespace Alma.Data;
public record ItemToLink(string SourcePath, string TargetPath);

View File

@@ -0,0 +1,6 @@
namespace Alma.Services;
public interface IConfigurationFileReader
{
public Task<(T? Result, string? FileName)> DeserializeAsync<T>(string fileNameWithoutExtension, string? extension = null) where T : class;
}

View File

@@ -0,0 +1,7 @@
namespace Alma.Services;
public interface IFolderService
{
string? ConfigRoot { get; }
string AppData { get; }
}

View File

@@ -0,0 +1,8 @@
using Alma.Data;
namespace Alma.Services;
public interface IMetadataHandler
{
Task SaveLinkedItemsAsync(List<ItemToLink> successfulLinks, DirectoryInfo sourceDirectory, DirectoryInfo targetDirectory);
}

View File

@@ -0,0 +1,8 @@
using Alma.Configuration.Module;
namespace Alma.Services;
public interface IModuleConfigurationResolver
{
Task<(ModuleConfiguration? mergedModuleConfig, string? moduleConfigFileName)> ResolveModuleConfiguration(string moduleConfigStub);
}

View File

@@ -0,0 +1,7 @@
namespace Alma.Services;
public interface IOsInformation
{
string GetOsIdentifier();
bool IsOnPlatform(string platform);
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Alma.Abstraction\Alma.Abstraction.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Alma</RootNamespace>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,37 @@
using Alma.Command;
using Alma.Command.Help;
namespace Alma;
public class Application
{
private readonly IList<ICommand> _commands;
public Application(IEnumerable<ICommand> commands)
{
_commands = commands.Append(new HelpCommand(() => _commands!)).ToList();
}
public async Task Run(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("No command was given");
return;
}
var commandString = args[0];
var command = _commands.FirstOrDefault(c => c.CommandString == commandString);
if (command is null)
{
Console.WriteLine($"Invalid command: {commandString}");
return;
}
await command.Run(args[1..].ToList());
return;
}
}

View File

@@ -0,0 +1,24 @@
namespace Alma.Command.Help;
public class HelpCommand : ICommand
{
private readonly Func<IEnumerable<ICommand>> _commandsProvider;
public string CommandString => "help";
public HelpCommand(Func<IEnumerable<ICommand>> commandsProvider)
{
_commandsProvider = commandsProvider;
}
public Task Run(List<string> parameters)
{
Console.WriteLine("Commands:" + Environment.NewLine);
foreach (var command in _commandsProvider().OrderBy(c => c.CommandString))
{
Console.WriteLine(command.CommandString);
}
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,10 @@
namespace Alma.Command.Info;
public class ModuleInfoCommand : ICommand
{
public string CommandString => "info";
public Task Run(List<string> parameters)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,193 @@
using Alma.Configuration.Module;
using Alma.Configuration.Repository;
using Alma.Data;
using Alma.Services;
namespace Alma.Command.Link;
public class LinkCommand : ICommand
{
private readonly IRepositoryConfiguration _repositoryConfiguration;
private readonly IModuleConfigurationResolver _moduleConfigurationResolver;
private readonly IFolderService _folderService;
private readonly IMetadataHandler _metadataHandler;
public string CommandString => "link";
public LinkCommand(
IRepositoryConfiguration repositoryConfiguration,
IModuleConfigurationResolver moduleConfigurationResolver,
IFolderService folderService,
IMetadataHandler metadataHandler)
{
_repositoryConfiguration = repositoryConfiguration;
_moduleConfigurationResolver = moduleConfigurationResolver;
_folderService = folderService;
_metadataHandler = metadataHandler;
}
public async Task Run(List<string> parameters)
{
if (parameters.Count == 0)
{
Console.WriteLine("No module specified");
return;
}
string moduleName = parameters[0];
string sourceDirectory = Path.Combine(Environment.CurrentDirectory);
string targetDirectory = Path.Combine(Environment.CurrentDirectory, "..");
var repoName = GetRepositoryName(parameters);
if (repoName is not null
&& _repositoryConfiguration.Configuration.Repositories.FirstOrDefault(r => r.Name == repoName) is { } repoConfig)
{
sourceDirectory = repoConfig.RepositoryPath ?? sourceDirectory;
targetDirectory = repoConfig.LinkPath ?? targetDirectory;
}
if (!Directory.Exists(sourceDirectory))
{
Console.WriteLine("Source directory not exists: " + sourceDirectory);
return;
}
string moduleNameAsPath = moduleName.Replace('/', Path.DirectorySeparatorChar);
string moduleDirectory = Path.Combine(sourceDirectory, moduleNameAsPath);
if (!Directory.Exists(moduleDirectory))
{
Console.WriteLine("Module directory not exists: " + moduleDirectory);
return;
}
var moduleConfigFileStub = Path.Combine(moduleDirectory, Constants.ModuleConfigFileStub);
var (moduleConfiguration, moduleConfigurationFile) = await _moduleConfigurationResolver.ResolveModuleConfiguration(moduleConfigFileStub);
if (moduleConfiguration?.Target is string moduleTargetDir)
{
targetDirectory = ResolvePath(moduleTargetDir, targetDirectory);
}
if (!Directory.Exists(targetDirectory))
{
Directory.CreateDirectory(targetDirectory);
}
var moduleConfigurationFileFullPath = moduleConfigurationFile is null ? null : Path.Combine(moduleDirectory, moduleConfigurationFile);
var moduleDir = new DirectoryInfo(moduleDirectory);
var currentTargetDirectory = new DirectoryInfo(targetDirectory);
var itemsToLink = (await TraverseTree(
moduleDir,
currentTargetDirectory,
moduleDir,
currentTargetDirectory,
moduleConfiguration)).ToList();
if (moduleConfigurationFile is not null) itemsToLink.RemoveAll(i => i.SourcePath == moduleConfigurationFileFullPath);
foreach (var itemToLink in itemsToLink)
{
Console.WriteLine($"Link: '{itemToLink.SourcePath}' '{itemToLink.TargetPath}'");
}
var successfulLinks = CreateLinks(itemsToLink);
await _metadataHandler.SaveLinkedItemsAsync(successfulLinks, moduleDir, currentTargetDirectory);
//await _metadataHandler.SaveLinkedItemsAsync(itemsToLink, moduleDir, currentTargetDirectory);
}
private List<ItemToLink> CreateLinks(List<ItemToLink> itemsToLink)
{
var successfulLinks = new List<ItemToLink>();
foreach (var itemToLink in itemsToLink)
{
if (File.Exists(itemToLink.TargetPath) || Directory.Exists(itemToLink.TargetPath))
{
Console.WriteLine("Item already exists: " + itemToLink.TargetPath);
continue;
}
var sourceFileExists = File.Exists(itemToLink.SourcePath);
var sourceDirectoryExists = Directory.Exists(itemToLink.SourcePath);
if (sourceFileExists)
{
File.CreateSymbolicLink(itemToLink.TargetPath, itemToLink.SourcePath);
}
else if (sourceDirectoryExists)
{
File.CreateSymbolicLink(itemToLink.TargetPath, itemToLink.SourcePath);
}
else
{
Console.WriteLine("Source not exists: " + itemToLink.SourcePath);
continue;
}
successfulLinks.Add(itemToLink);
}
return successfulLinks;
}
private async Task<IEnumerable<ItemToLink>> TraverseTree(
DirectoryInfo currentDirectory,
DirectoryInfo currentTargetDirectory,
DirectoryInfo moduleDirectory,
DirectoryInfo targetDirectory,
ModuleConfiguration? moduleConfiguration)
{
var filesToLink = new List<ItemToLink>();
foreach (var file in currentDirectory.GetFiles())
{
filesToLink.Add(new ItemToLink(Path.Combine(currentDirectory.FullName, file.Name), Path.Combine(currentTargetDirectory.FullName, file.Name)));
}
var subDirLinksToAdd = Enumerable.Empty<ItemToLink>();
foreach (var subDir in currentDirectory.GetDirectories())
{
var relativePath = GetRelativePath(subDir.FullName, moduleDirectory.FullName);
if (moduleConfiguration?.Links?.ContainsKey(relativePath) ?? false)
{
filesToLink.Add(new ItemToLink(subDir.FullName, ResolvePath(moduleConfiguration.Links[relativePath], targetDirectory.FullName)));
}
else
{
var subDirLinks = await TraverseTree(
subDir,
new DirectoryInfo(Path.Combine(currentTargetDirectory.FullName, subDir.Name)),
moduleDirectory,
targetDirectory,
moduleConfiguration
);
subDirLinksToAdd = subDirLinksToAdd.Concat(subDirLinks);
}
}
return filesToLink.Concat(subDirLinksToAdd);
}
private static string? GetRepositoryName(List<string> parameters)
{
//TODO: handle parameters
if (parameters.Count < 2) return null;
return parameters[1];
}
private static string ResolvePath(string path, string currentDirectory)
{
if (path.StartsWith("~"))
{
path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), path.Substring(2));
}
//TODO: more special character
return Path.Combine(currentDirectory, path);
}
private static string GetRelativePath(string full, string parent) => full.Substring(parent.Length).TrimStart(Path.DirectorySeparatorChar);
}

View File

@@ -0,0 +1,90 @@
using Alma.Configuration.Repository;
using Alma.Data;
using Alma.Services;
namespace Alma.Command.List;
public class ListCommand : ICommand
{
private readonly IRepositoryConfiguration _repositoryConfiguration;
private readonly IModuleConfigurationResolver _moduleConfigurationResolver;
public string CommandString => "ls";
public ListCommand(
IRepositoryConfiguration repositoryConfiguration,
IModuleConfigurationResolver moduleConfigurationResolver)
{
_repositoryConfiguration = repositoryConfiguration;
_moduleConfigurationResolver = moduleConfigurationResolver;
}
public async Task Run(List<string> parameters)
{
if (parameters.Count > 0)
{
await ListModulesByRepoName(parameters[0]);
}
else
{
await ListRepositories();
}
}
private async Task ListRepositories()
{
Console.WriteLine("Repositories:" + Environment.NewLine);
foreach (var repository in _repositoryConfiguration.Configuration.Repositories)
{
Console.WriteLine(repository.Name);
}
}
private async Task ListModulesByRepoName(string repositoryName)
{
var repo = _repositoryConfiguration.Configuration.Repositories.FirstOrDefault(r => r.Name == repositoryName);
if (repo is null)
{
Console.WriteLine($"No repository found with name '{repositoryName}'");
return;
}
if (repo.RepositoryPath is null)
{
Console.WriteLine($"No repository path is specified in repository settings '{repositoryName}'");
return;
}
await ListModules(repo.RepositoryPath, repositoryName);
}
private async Task ListModules(string repositoryPath, string repositoryName)
{
var repositoryDirectory = new DirectoryInfo(repositoryPath);
var moduleDirectories = await TraverseRepositoryFolder(repositoryDirectory);
Console.WriteLine($"Modules in repository '{repositoryName}':" + Environment.NewLine);
foreach (var modulePath in moduleDirectories)
{
Console.WriteLine(modulePath.FullName.Substring(repositoryDirectory.FullName.Length).Replace(Path.DirectorySeparatorChar, '/'));
}
}
private async Task<IEnumerable<DirectoryInfo>> TraverseRepositoryFolder(DirectoryInfo currentDirectory)
{
var moduleConfigFileStub = Path.Combine(currentDirectory.FullName, Constants.ModuleConfigFileStub);
var (moduleConfiguration, moduleConfigurationFile) = await _moduleConfigurationResolver.ResolveModuleConfiguration(moduleConfigFileStub);
var result = Enumerable.Empty<DirectoryInfo>();
if (moduleConfigurationFile is not null)
{
result = new List<DirectoryInfo> {currentDirectory};
}
foreach (var subDir in currentDirectory.GetDirectories())
{
result = result.Concat(await TraverseRepositoryFolder(subDir));
}
return result;
}
}

View File

@@ -0,0 +1,10 @@
namespace Alma.Command.Unlink;
public class UnlinkCommand : ICommand
{
public string CommandString => "unlink";
public Task Run(List<string> parameters)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,38 @@
using Alma.Services;
namespace Alma.Configuration.Repository;
public class RepositoryConfiguration : IRepositoryConfiguration
{
private readonly IFolderService _folderService;
private readonly ConfigurationFileReader _configurationFileReader;
public RepositoryConfigurationRoot Configuration { get; private set; } = new RepositoryConfigurationRoot(new List<RepositoryConfigurationEntry>());
public RepositoryConfiguration(IFolderService folderService, ConfigurationFileReader configurationFileReader)
{
_folderService = folderService;
_configurationFileReader = configurationFileReader;
}
public async Task LoadAsync()
{
if (_folderService.ConfigRoot is null)
{
Configuration = new RepositoryConfigurationRoot(new List<RepositoryConfigurationEntry>());
return;
}
var repoConfigFileNameStub = Path.Combine(_folderService.ConfigRoot, "repository");
var (configuration, repoConfigFileName) = await _configurationFileReader.DeserializeAsync<RepositoryConfigurationRoot>(repoConfigFileNameStub);
Configuration = configuration ?? new RepositoryConfigurationRoot(new List<RepositoryConfigurationEntry>());
foreach (var repositoryConfigurationEntry in Configuration.Repositories)
{
if (repositoryConfigurationEntry.Name is null)
{
Console.WriteLine($"Entry name is null in {repoConfigFileName}");
}
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Alma.Data.Repository;
public class RepositoryFolder
{
}

View File

@@ -0,0 +1,19 @@
using System.Security.Cryptography;
using System.Text;
namespace Alma.Helper;
public static class MD5Helper
{
public static string GetMD5Hash(string source)
{
using var md5Hasher = MD5.Create();
var data = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(source));
var sBuilder = new StringBuilder();
for (var i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
return sBuilder.ToString();
}
}

View File

@@ -0,0 +1,21 @@
namespace Alma.Services;
public class ConfigurationFileReader
{
private readonly List<IConfigurationFileReader> _configurationFileReaders;
public ConfigurationFileReader(IEnumerable<IConfigurationFileReader> configurationFileReaders)
{
_configurationFileReaders = configurationFileReaders.ToList();
}
public async Task<(T? Result, string? FileName)> DeserializeAsync<T>(string fileNameWithoutExtension, string? extension = null) where T : class
{
foreach (var configurationFileReader in _configurationFileReaders)
{
if (await configurationFileReader.DeserializeAsync<T>(fileNameWithoutExtension, extension) is {Result: { }} result) return result;
}
return (null, null);
}
}

View File

@@ -0,0 +1,51 @@
namespace Alma.Services;
public class FolderService : IFolderService
{
public string? ConfigRoot { get; }
public string AppData { get; }
public FolderService()
{
ConfigRoot = GetConfigHomePath();
AppData = GetAppDataPath();
if (!Directory.Exists(AppData)) Directory.CreateDirectory(AppData);
}
private static string? GetConfigHomePath()
{
var configHomeProviders = new List<Func<string?>>
{
() => Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"),
() => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config")
};
var configHome = EnumerateProviders(configHomeProviders);
return configHome == null ? null : Path.Combine(configHome, "alma");
}
private static string GetAppDataPath()
{
var appDataProviders = new List<Func<string?>>
{
() => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
};
var appData = EnumerateProviders(appDataProviders) ?? Environment.CurrentDirectory;
return Path.Combine(appData, "alma");
}
private static string? EnumerateProviders(List<Func<string?>> providers)
{
string? result = null;
foreach (var provider in providers)
{
result = provider();
if (result is not null && Directory.Exists(result)) break;
}
return result;
}
}

View File

@@ -0,0 +1,18 @@
using System.Text.Json;
namespace Alma.Services;
public class JsonConfigurationFileReader : IConfigurationFileReader
{
private static readonly JsonSerializerOptions DefaultOptions = new(JsonSerializerDefaults.Web);
public async Task<(T? Result, string? FileName)> DeserializeAsync<T>(string fileNameWithoutExtension, string? extension) where T : class
{
extension ??= "json";
var fileName = fileNameWithoutExtension + "." + extension;
if (!File.Exists(fileName)) return (null, null);
await using FileStream openStream = File.OpenRead(fileName);
return (await JsonSerializer.DeserializeAsync<T>(openStream, DefaultOptions), fileName);
}
}

View File

@@ -0,0 +1,66 @@
using System.Text;
using Alma.Data;
using Alma.Helper;
namespace Alma.Services;
public class MetadataHandler : IMetadataHandler
{
private const string MetadataFilename = "moduleHashes.txt";
private readonly IFolderService _folderService;
public MetadataHandler(IFolderService folderService)
{
_folderService = folderService;
}
public async Task SaveLinkedItemsAsync(List<ItemToLink> successfulLinks, DirectoryInfo sourceDirectory, DirectoryInfo targetDirectory)
{
var sourcePathHash = MD5Helper.GetMD5Hash(sourceDirectory.FullName);
var targetPathHash = MD5Helper.GetMD5Hash(targetDirectory.FullName);
var modulePathHash = MD5Helper.GetMD5Hash(sourcePathHash + targetPathHash);
var appDataDirectory = new DirectoryInfo(_folderService.AppData);
var moduleFolderMetadataPath = Path.Combine(appDataDirectory.FullName, modulePathHash + ".txt");
var previousData = new List<string>();
if (File.Exists(moduleFolderMetadataPath))
{
var content = await File.ReadAllLinesAsync(moduleFolderMetadataPath);
previousData.AddRange(content.Skip(1));
}
var newContent = previousData.Concat(successfulLinks.Select(s => s.TargetPath)).Distinct();
await File.WriteAllLinesAsync(moduleFolderMetadataPath, newContent.Prepend("text"));
//TODO write md5 & path to a common file
var hashAlreadySaved = false;
var metadataFilePath = Path.Combine(_folderService.AppData, MetadataFilename);
if (File.Exists(metadataFilePath))
{
await using var metadataFileStream = File.OpenRead(metadataFilePath);
using var metadataFileStream2 = new StreamReader(metadataFileStream);
while (await metadataFileStream2.ReadLineAsync() is { } s)
{
if (!s.StartsWith(modulePathHash)) continue;
hashAlreadySaved = true;
break;
}
}
if (!hashAlreadySaved)
{
var newLineContent = modulePathHash + " " + EncodePath(sourceDirectory.FullName) + " " + EncodePath(targetDirectory.FullName);
await File.AppendAllLinesAsync(metadataFilePath, new[] {newLineContent});
}
static string EncodePath(string path)
{
byte[] toEncodeAsBytes = Encoding.UTF8.GetBytes(path);
return Convert.ToBase64String(toEncodeAsBytes);
}
}
}

View File

@@ -0,0 +1,35 @@
using Alma.Configuration.Module;
namespace Alma.Services;
public class ModuleConfigurationResolver : IModuleConfigurationResolver
{
private readonly IConfigurationFileReader _configurationFileReader;
private readonly IOsInformation _osInformation;
public ModuleConfigurationResolver(
IConfigurationFileReader configurationFileReader,
IOsInformation osInformation)
{
_configurationFileReader = configurationFileReader;
_osInformation = osInformation;
}
public async Task<(ModuleConfiguration? mergedModuleConfig, string? moduleConfigFileName)> ResolveModuleConfiguration(string moduleConfigStub)
{
var (moduleConfigRoot, moduleConfigFileName) = await _configurationFileReader.DeserializeAsync<ModuleConfigurationRoot>(moduleConfigStub);
if (moduleConfigRoot is null) return (null, null);
var validModuleConfigurations = moduleConfigRoot.Where(m => _osInformation.IsOnPlatform(m.Key));
//TODO: priority order
var orderedValidModuleConfigurations = new Dictionary<string, ModuleConfiguration>(validModuleConfigurations);
var mergedModuleConfig = orderedValidModuleConfigurations
.Select(m => m.Value)
.Aggregate((a, b) => a.Merge(b));
return (mergedModuleConfig, moduleConfigFileName);
}
}

View File

@@ -0,0 +1,24 @@
using System.Runtime.InteropServices;
namespace Alma.Services;
public class OsInformation : IOsInformation
{
private const string OsIdentifierDefault = "default";
private const string OsIdentifierWin = "windows";
private const string OsIdentifierLinux = "linux";
public string GetOsIdentifier()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return OsIdentifierWin;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return OsIdentifierLinux;
return "unknown";
}
public bool IsOnPlatform(string platform)
{
if (platform == OsIdentifierDefault) return true;
return platform == GetOsIdentifier();
}
}

34
src/Alma.sln Normal file
View File

@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alma.Abstraction", "Alma.Abstraction\Alma.Abstraction.csproj", "{49A2563D-8D89-4B2A-A81E-39A50F5B0C25}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alma.App", "Alma.App\Alma.App.csproj", "{6FBB9920-A249-41AE-8CE2-5D2A4FF3B551}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alma", "Alma\Alma.csproj", "{23157A6F-C737-4ED4-B36B-BFE3EA31EAF1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{49A2563D-8D89-4B2A-A81E-39A50F5B0C25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49A2563D-8D89-4B2A-A81E-39A50F5B0C25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49A2563D-8D89-4B2A-A81E-39A50F5B0C25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49A2563D-8D89-4B2A-A81E-39A50F5B0C25}.Release|Any CPU.Build.0 = Release|Any CPU
{6FBB9920-A249-41AE-8CE2-5D2A4FF3B551}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6FBB9920-A249-41AE-8CE2-5D2A4FF3B551}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6FBB9920-A249-41AE-8CE2-5D2A4FF3B551}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6FBB9920-A249-41AE-8CE2-5D2A4FF3B551}.Release|Any CPU.Build.0 = Release|Any CPU
{23157A6F-C737-4ED4-B36B-BFE3EA31EAF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23157A6F-C737-4ED4-B36B-BFE3EA31EAF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23157A6F-C737-4ED4-B36B-BFE3EA31EAF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23157A6F-C737-4ED4-B36B-BFE3EA31EAF1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

18
src/Alma/Alma.csproj Normal file
View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Alma.App\Alma.App.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Jab" Version="0.8.4" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

81
src/Alma/Program.cs Normal file
View File

@@ -0,0 +1,81 @@
using Alma.Command;
using Alma.Command.Help;
using Alma.Command.Info;
using Alma.Command.Link;
using Alma.Command.List;
using Alma.Command.Unlink;
using Alma.Configuration.Repository;
using Alma.Services;
using Jab;
namespace Alma;
public static class Program
{
/*public static async Task Main(string[] args)
{
var services = BuildServices();
var repositoryConfiguration = services.GetRequiredService<IRepositoryConfiguration>();
await repositoryConfiguration.LoadAsync();
var application = services.GetRequiredService<Application>();
await application.Run(args);
static IServiceProvider BuildServices()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IRepositoryConfiguration, RepositoryConfiguration>();
serviceCollection.AddSingleton<IFolderService, FolderService>();
serviceCollection.AddSingleton<ConfigurationFileReader>();
serviceCollection.AddSingleton<IConfigurationFileReader, JsonConfigurationFileReader>();
serviceCollection.AddSingleton<IOsInformation, OsInformation>();
serviceCollection.AddSingleton<ICommand, LinkCommand>();
serviceCollection.AddSingleton<IModuleConfigurationResolver, ModuleConfigurationResolver>();
serviceCollection.AddSingleton<Application>();
typeof(IRepositoryConfiguration), typeof(RepositoryConfiguration)
typeof(IFolderService), typeof(FolderService)
typeof(ConfigurationFileReader)
typeof(IConfigurationFileReader), typeof(JsonConfigurationFileReader)
typeof(IOsInformation), typeof(OsInformation)
typeof(ICommand), typeof(LinkCommand)
typeof(IModuleConfigurationResolver), typeof(ModuleConfigurationResolver)
typeof(Application)
return serviceCollection.BuildServiceProvider();
}
}*/
public static async Task Main(string[] args)
{
var services = new AlmaServiceProvider();
var repositoryConfiguration = services.GetService<IRepositoryConfiguration>();
await repositoryConfiguration.LoadAsync();
var application = services.GetService<Application>();
await application.Run(args);
}
}
[ServiceProvider]
[Singleton(typeof(IRepositoryConfiguration), typeof(RepositoryConfiguration))]
[Singleton(typeof(IFolderService), typeof(FolderService))]
[Singleton(typeof(ConfigurationFileReader))]
[Singleton(typeof(IConfigurationFileReader), typeof(JsonConfigurationFileReader))]
[Singleton(typeof(IOsInformation), typeof(OsInformation))]
[Singleton(typeof(ICommand), typeof(LinkCommand))]
[Singleton(typeof(ICommand), typeof(UnlinkCommand))]
[Singleton(typeof(ICommand), typeof(ModuleInfoCommand))]
[Singleton(typeof(ICommand), typeof(ListCommand))]
//Dependency cycle
//[Singleton(typeof(ICommand), typeof(HelpCommand))]
[Singleton(typeof(IModuleConfigurationResolver), typeof(ModuleConfigurationResolver))]
[Singleton(typeof(IMetadataHandler), typeof(MetadataHandler))]
[Singleton(typeof(Application))]
internal partial class AlmaServiceProvider
{
}