Add shared packages

This commit is contained in:
2025-06-02 08:58:34 -03:00
parent 6ebfde013a
commit 5b2588b47f
32 changed files with 981 additions and 7 deletions

View File

@ -5,9 +5,18 @@
<ItemGroup>
<PackageVersion Include="dotenv.net" Version="3.2.0" />
<PackageVersion Include="JsonSubTypes" Version="2.0.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.16" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Polly" Version="8.1.0" />
<PackageVersion Include="RestSharp" Version="112.1.0" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="Tomlyn.Extensions.Configuration" Version="1.0.6" />
<PackageVersion Include="Tomlyn" Version="0.17.0" />
</ItemGroup>
</Project>

View File

@ -7,6 +7,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Generated", "src\Tr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Example", "src\Tribufu.Example\Tribufu.Example.csproj", "{D6392A29-E2DC-4050-B4C1-B279DD2D226D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Logging", "src\Tribufu.Logging\Tribufu.Logging.csproj", "{CFD80847-9B98-4991-BADF-8714E7D8D81C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Configuration", "src\Tribufu.Configuration\Tribufu.Configuration.csproj", "{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Runtime", "src\Tribufu.Runtime\Tribufu.Runtime.csproj", "{26EEB407-733C-4383-9211-B083CD5F593B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Database", "src\Tribufu.Database\Tribufu.Database.csproj", "{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Serialization", "src\Tribufu.Serialization\Tribufu.Serialization.csproj", "{D6DAE078-2F80-49DD-97A3-B1223FE04F91}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.ComponentModel", "src\Tribufu.ComponentModel\Tribufu.ComponentModel.csproj", "{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -53,6 +65,78 @@ Global
{D6392A29-E2DC-4050-B4C1-B279DD2D226D}.Release|x64.Build.0 = Release|Any CPU
{D6392A29-E2DC-4050-B4C1-B279DD2D226D}.Release|x86.ActiveCfg = Release|Any CPU
{D6392A29-E2DC-4050-B4C1-B279DD2D226D}.Release|x86.Build.0 = Release|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Debug|x64.ActiveCfg = Debug|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Debug|x64.Build.0 = Debug|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Debug|x86.ActiveCfg = Debug|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Debug|x86.Build.0 = Debug|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Release|Any CPU.Build.0 = Release|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Release|x64.ActiveCfg = Release|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Release|x64.Build.0 = Release|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Release|x86.ActiveCfg = Release|Any CPU
{CFD80847-9B98-4991-BADF-8714E7D8D81C}.Release|x86.Build.0 = Release|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Debug|x64.ActiveCfg = Debug|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Debug|x64.Build.0 = Debug|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Debug|x86.ActiveCfg = Debug|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Debug|x86.Build.0 = Debug|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Release|Any CPU.Build.0 = Release|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Release|x64.ActiveCfg = Release|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Release|x64.Build.0 = Release|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Release|x86.ActiveCfg = Release|Any CPU
{C0A841C8-9FC5-4AC0-B9AD-6BBFCEDCBE5F}.Release|x86.Build.0 = Release|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Debug|x64.ActiveCfg = Debug|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Debug|x64.Build.0 = Debug|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Debug|x86.ActiveCfg = Debug|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Debug|x86.Build.0 = Debug|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Release|Any CPU.Build.0 = Release|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Release|x64.ActiveCfg = Release|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Release|x64.Build.0 = Release|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Release|x86.ActiveCfg = Release|Any CPU
{26EEB407-733C-4383-9211-B083CD5F593B}.Release|x86.Build.0 = Release|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Debug|x64.ActiveCfg = Debug|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Debug|x64.Build.0 = Debug|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Debug|x86.ActiveCfg = Debug|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Debug|x86.Build.0 = Debug|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Release|Any CPU.Build.0 = Release|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Release|x64.ActiveCfg = Release|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Release|x64.Build.0 = Release|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Release|x86.ActiveCfg = Release|Any CPU
{E7F9A76F-C087-410B-B4B5-A928A6CDC2BA}.Release|x86.Build.0 = Release|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Debug|x64.ActiveCfg = Debug|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Debug|x64.Build.0 = Debug|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Debug|x86.ActiveCfg = Debug|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Debug|x86.Build.0 = Debug|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Release|Any CPU.Build.0 = Release|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Release|x64.ActiveCfg = Release|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Release|x64.Build.0 = Release|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Release|x86.ActiveCfg = Release|Any CPU
{D6DAE078-2F80-49DD-97A3-B1223FE04F91}.Release|x86.Build.0 = Release|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Debug|x64.ActiveCfg = Debug|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Debug|x64.Build.0 = Debug|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Debug|x86.ActiveCfg = Debug|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Debug|x86.Build.0 = Debug|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|Any CPU.Build.0 = Release|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|x64.ActiveCfg = Release|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|x64.Build.0 = Release|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|x86.ActiveCfg = Release|Any CPU
{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

5
scripts/package.ps1 Normal file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env sh
dotnet clean
dotnet build -c Release
dotnet pack

View File

@ -1,3 +0,0 @@
#!/usr/bin/env sh
dotnet pack

View File

@ -0,0 +1,30 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization;
namespace Tribufu.ComponentModel
{
public class EnumMemberConverter<T> : EnumConverter
{
public EnumMemberConverter(Type type) : base(type) { }
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var type = typeof(T);
foreach (var field in type.GetFields())
{
if (Attribute.GetCustomAttribute(field, typeof(EnumMemberAttribute)) is EnumMemberAttribute attribute && value is string enumValue && attribute.Value == enumValue)
{
return field.GetValue(null);
}
}
return base.ConvertFrom(context, culture, value);
}
}
}

View File

@ -0,0 +1 @@
# Tribufu

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Tribufu.ComponentModel</PackageId>
<Description>Tribufu ComponentModel Extensions</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup>
<AppDesignerFolder>Properties</AppDesignerFolder>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<OutputType>Library</OutputType>
<TargetFrameworks>netstandard2.0;net45;net5.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,53 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using Microsoft.Extensions.Configuration;
using System.IO;
using Tomlyn.Extensions.Configuration;
using Tribufu.Logging;
using Tribufu.Runtime;
namespace Tribufu.Configuration
{
public static class ConfigurationManager
{
public static IConfiguration Configuration { get; private set; }
public static IConfiguration Load(string[] fileNames)
{
var configDirectory = ApplicationContext.GetConfigDirectory();
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddEnvironmentVariables();
foreach (var fileName in fileNames)
{
var fullPath = Path.Combine(configDirectory, fileName);
if (!File.Exists(fullPath))
{
Logger.Debug($"Config file '{fullPath}' not found, skipping.");
continue;
}
var ext = Path.GetExtension(fullPath).ToLowerInvariant();
switch (ext)
{
case ".ini":
configurationBuilder.AddIniFile(fullPath, true, false);
break;
case ".json":
configurationBuilder.AddJsonFile(fullPath, true, false);
break;
case ".toml":
configurationBuilder.AddTomlFile(fullPath, true, false);
break;
default:
Logger.Warn($"Unsupported config file extension: {ext}");
break;
}
}
Configuration = configurationBuilder.Build();
return Configuration;
}
}
}

View File

@ -0,0 +1 @@
# Tribufu

View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Tribufu.Configuration</PackageId>
<Description>Tribufu Configuration Extensions</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup>
<AppDesignerFolder>Properties</AppDesignerFolder>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<OutputType>Library</OutputType>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Tomlyn.Extensions.Configuration" />
<PackageReference Include="Tomlyn" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Tribufu.Logging\Tribufu.Logging.csproj" />
<ProjectReference Include="..\Tribufu.Runtime\Tribufu.Runtime.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,55 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using Microsoft.Extensions.Configuration;
using System;
namespace Tribufu.Database
{
public class DatabaseConfiguration
{
public DatabaseDriver Driver { get; set; }
public string? Version { get; set; }
public string? Host { get; set; }
public string? Port { get; set; }
public string? User { get; set; }
public string? Password { get; set; }
public string? Schema { get; set; }
/// <summary>
/// Loads the <see cref="DatabaseConfiguration"/> from the "database" section or from root-level keys prefixed with "database_".
/// </summary>
/// <param name="configuration">The configuration source.</param>
/// <returns>The populated <see cref="DatabaseConfiguration"/> instance.</returns>
public static DatabaseConfiguration Load(IConfiguration configuration)
{
var section = configuration.GetSection("database");
var useRootFallback = !section.Exists();
string? GetConfig(string key) => useRootFallback ? configuration[$"database_{key}"] : section[key];
var driverString = GetConfig("driver") ?? throw new Exception("Missing database driver");
if (!Enum.TryParse<DatabaseDriver>(driverString, true, out var driver))
{
throw new Exception($"Unsupported database driver: {driverString}");
}
return new DatabaseConfiguration
{
Driver = driver,
Version = GetConfig("version"),
Host = GetConfig("host"),
Port = GetConfig("port"),
User = GetConfig("user"),
Password = GetConfig("password"),
Schema = GetConfig("schema")
};
}
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
namespace Tribufu.Database
{
public static class DatabaseConstants
{
public const uint DEFAULT_PAGINATION = 20;
public const uint MAX_PAGINATION = 100;
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
namespace Tribufu.Database
{
public enum DatabaseDriver : byte
{
MySql = 0,
Postgres = 1,
SqlServer = 2,
Oracle = 3,
Firebird = 4,
Sqlite = 5,
MongoDb = 6,
}
}

View File

@ -0,0 +1 @@
# Tribufu

View File

@ -0,0 +1,39 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Tribufu.Database.Repositories
{
public interface IRepository<T, K> where T : class
{
IList<T> GetAll();
Task<IList<T>> GetAllAsync();
IList<T> GetPage(uint page, uint limit);
Task<IList<T>> GetPageAsync(uint page, uint limit);
T? GetOne(K key);
Task<T?> GetOneAsync(K key);
T? Create(T entity);
Task<T?> CreateAsync(T entity);
T? Update(T entity);
Task<T?> UpdateAsync(T entity);
void Delete(K key);
Task DeleteAsync(K key);
void Delete(T entity);
Task DeleteAsync(T entity);
}
}

View File

@ -0,0 +1,113 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Tribufu.Database.Repositories
{
public class Repository<C, T, K> : IRepository<T, K> where C : DbContext where T : class
{
protected readonly C _context;
protected readonly DbSet<T> _dbSet;
public Repository(C context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
_dbSet = context.Set<T>();
}
public virtual IList<T> GetAll()
{
return [.. _dbSet];
}
public virtual async Task<IList<T>> GetAllAsync()
{
return await _dbSet.ToListAsync();
}
public virtual IList<T> GetPage(uint page, uint limit)
{
return _dbSet.Skip((int)((page < 1 ? 0 : page - 1) * limit)).Take((int)limit).ToList();
}
public virtual async Task<IList<T>> GetPageAsync(uint page, uint limit)
{
return await _dbSet.Skip((int)((page < 1 ? 0 : page - 1) * limit)).Take((int)limit).ToListAsync();
}
public virtual T? GetOne(K key)
{
return _dbSet.Find(key);
}
public virtual async Task<T?> GetOneAsync(K key)
{
return await _dbSet.FindAsync(key);
}
public virtual T? Create(T entity)
{
_dbSet.Add(entity);
var result = _context.SaveChanges();
return result > 0 ? entity : null;
}
public virtual async Task<T?> CreateAsync(T entity)
{
await _dbSet.AddAsync(entity);
var result = await _context.SaveChangesAsync();
return result > 0 ? entity : null;
}
public virtual T? Update(T entity)
{
_dbSet.Update(entity);
var result = _context.SaveChanges();
return result > 0 ? entity : null;
}
public virtual async Task<T?> UpdateAsync(T entity)
{
_dbSet.Update(entity);
var result = await _context.SaveChangesAsync();
return result > 0 ? entity : null;
}
public virtual void Delete(K key)
{
var entity = _dbSet.Find(key);
if (entity != null)
{
Delete(entity);
}
}
public virtual async Task DeleteAsync(K key)
{
var entity = await _dbSet.FindAsync(key);
if (entity != null)
{
await DeleteAsync(entity);
}
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
_context.SaveChanges();
}
public virtual async Task DeleteAsync(T entity)
{
_dbSet.Remove(entity);
await _context.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Tribufu.Database</PackageId>
<Description>Tribufu Database Extensions</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup>
<AppDesignerFolder>Properties</AppDesignerFolder>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<TargetFrameworks>net8.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Tribufu.Configuration\Tribufu.Configuration.csproj" />
<ProjectReference Include="..\Tribufu.Logging\Tribufu.Logging.csproj" />
<ProjectReference Include="..\Tribufu.Runtime\Tribufu.Runtime.csproj" />
</ItemGroup>
</Project>

View File

@ -1,8 +1,9 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: MIT
using dotenv.net;
using Tribufu.Generated.Client;
using Tribufu.Logging;
namespace Tribufu.Test
{
@ -10,21 +11,23 @@ namespace Tribufu.Test
{
public static async Task Main(string[] args)
{
Logger.Initialize(LogLevel.All);
DotEnv.Load(new DotEnvOptions(ignoreExceptions: true, envFilePaths: [".env", "../../.env"]));
var apiKey = Environment.GetEnvironmentVariable("TRIBUFU_API_KEY");
var tribufu = new TribufuApi(apiKey ?? "");
Console.WriteLine(TribufuApi.GetVersion());
Logger.Debug(TribufuApi.GetVersion());
try
{
var result = await tribufu.GetUserInfoAsync();
Console.WriteLine(result);
Logger.Debug(result.ToString());
}
catch (ApiException e)
{
Console.WriteLine(e.Message);
Logger.Debug(e.Message);
}
}
}

View File

@ -9,6 +9,7 @@
<PackageReference Include="dotenv.net" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Tribufu.Logging\Tribufu.Logging.csproj" />
<ProjectReference Include="..\Tribufu\Tribufu.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,22 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
namespace Tribufu.Logging
{
public enum LogLevel : byte
{
Off = 0,
Trace = 1,
Debug = 2,
Info = 3,
Warn = 4,
Error = 5,
All = 6,
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using System;
namespace Tribufu.Logging
{
public static class Logger
{
private static LogLevel _level = LogLevel.Off;
public static void Initialize(LogLevel level = LogLevel.Off)
{
_level = level;
}
public static void Info(string message)
{
Log(LogLevel.Info, message, ConsoleColor.Green);
}
public static void Warn(string message)
{
Log(LogLevel.Warn, message, ConsoleColor.Yellow);
}
public static void Error(string message)
{
Log(LogLevel.Error, message, ConsoleColor.Red);
}
public static void Debug(string message)
{
Log(LogLevel.Debug, message, ConsoleColor.White);
}
public static void Trace(string message)
{
Log(LogLevel.Trace, message, ConsoleColor.Gray);
}
private static void Log(LogLevel level, string message, ConsoleColor color)
{
if (_level == LogLevel.Off)
{
return;
}
if (_level == LogLevel.All || level >= _level)
{
var defaultColor = Console.ForegroundColor;
Console.ForegroundColor = color;
var timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");
Console.WriteLine($"[{timestamp}] [{level.ToString().ToUpper()}]: {message}");
Console.ForegroundColor = defaultColor;
}
}
}
}

View File

@ -0,0 +1 @@
# Tribufu

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Tribufu.Logging</PackageId>
<Description>Tribufu Logging Extensions</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup>
<AppDesignerFolder>Properties</AppDesignerFolder>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<OutputType>Library</OutputType>
<TargetFrameworks>netstandard2.0;net45;net5.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
</Project>

View File

@ -0,0 +1,183 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using System;
using System.IO;
using System.Runtime.InteropServices;
using Tribufu.Logging;
namespace Tribufu.Runtime
{
/// <summary>
/// Provides standardized access to important application directories, such as config, saved data, logs, and platform-specific binaries.
/// This is especially useful for abstracting file path logic across environments (development, production, etc).
/// </summary>
public static class ApplicationContext
{
/// <summary>
/// Gets the root base directory of the application.
/// </summary>
/// <remarks>
/// - In development, this resolves to the root of the repository (five levels above bin/Debug or bin/Release).
/// - In production, it resolves to two levels above the binary location.
/// - It uses case-insensitive checks and runtime heuristics to improve accuracy.
/// </remarks>
/// <returns>The absolute path to the base directory.</returns>
public static string GetBaseDirectory()
{
try
{
string baseDirectory;
string defaultBaseDirectory = AppContext.BaseDirectory;
bool isDevelopment = defaultBaseDirectory.ToLowerInvariant().Contains("debug");
if (isDevelopment)
{
// Go 5 levels up to simulate project root
baseDirectory = Path.Combine(defaultBaseDirectory, "..", "..", "..", "..", "..");
}
else
{
baseDirectory = Path.Combine(defaultBaseDirectory, "..", "..");
}
return Path.GetFullPath(baseDirectory);
}
catch (Exception ex)
{
Logger.Warn($"(ApplicationContext) Failed to resolve base directory: {ex.Message}");
return AppContext.BaseDirectory;
}
}
/// <summary>
/// Gets the path to the platform-specific binary directory.
/// </summary>
/// <returns>
/// The absolute path to <c>bin/&lt;runtime-identifier&gt;</c> if available,
/// otherwise falls back to <c>bin/dotnet</c>.
/// </returns>
public static string GetBinDirectory()
{
var binDirectory = Path.Combine(GetBaseDirectory(), "bin");
#if NETSTANDARD
var runtimeIdentifier = GetRuntimeIdentifierLegacy();
if (!string.IsNullOrEmpty(runtimeIdentifier))
{
binDirectory = Path.Combine(binDirectory, runtimeIdentifier);
}
else
{
binDirectory = Path.Combine(binDirectory, "dotnet");
}
#else
if (!string.IsNullOrEmpty(RuntimeInformation.RuntimeIdentifier))
{
binDirectory = Path.Combine(binDirectory, RuntimeInformation.RuntimeIdentifier);
}
else
{
binDirectory = Path.Combine(binDirectory, "dotnet");
}
#endif
return binDirectory;
}
private static string GetRuntimeIdentifierLegacy()
{
string osPart;
PlatformID platform = Environment.OSVersion.Platform;
switch (platform)
{
case PlatformID.Win32NT:
osPart = "win";
break;
case PlatformID.Unix:
if (IsMacOS())
osPart = "osx";
else
osPart = "linux";
break;
case PlatformID.MacOSX:
osPart = "osx";
break;
default:
osPart = "unknown";
break;
}
var archPart = Environment.Is64BitProcess ? "x64" : "x86";
if (osPart == "unknown")
{
return null;
}
return $"{osPart}-{archPart}";
}
private static bool IsMacOS()
{
if (Environment.OSVersion.Platform == PlatformID.Unix)
{
Version version = Environment.OSVersion.Version;
if (version.Major >= 19)
{
return true;
}
}
return false;
}
/// <summary>
/// Gets the path to the configuration directory.
/// </summary>
/// <returns>The absolute path to the <c>config</c> directory.</returns>
public static string GetConfigDirectory()
{
return Path.Combine(GetBaseDirectory(), "config");
}
/// <summary>
/// Gets the path to the assets directory.
/// </summary>
/// <returns>The absolute path to the <c>assets</c> directory.</returns>
public static string GetAssetsDirectory()
{
return Path.Combine(GetBaseDirectory(), "assets");
}
/// <summary>
/// Gets the path to the saved data directory.
/// </summary>
/// <returns>The absolute path to the <c>saved</c> directory.</returns>
public static string GetSavedDirectory()
{
return Path.Combine(GetBaseDirectory(), "saved");
}
/// <summary>
/// Gets the path to the cache directory inside <c>saved</c>.
/// </summary>
/// <returns>The absolute path to the <c>saved/cache</c> directory.</returns>
public static string GetCacheDirectory()
{
return Path.Combine(GetSavedDirectory(), "cache");
}
/// <summary>
/// Gets the path to the logs directory inside <c>saved</c>.
/// </summary>
/// <returns>The absolute path to the <c>saved/logs</c> directory.</returns>
public static string GetLogsDirectory()
{
return Path.Combine(GetSavedDirectory(), "logs");
}
}
}

View File

@ -0,0 +1 @@
# Tribufu

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Tribufu.Runtime</PackageId>
<Description>Tribufu Runtime Extensions</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup>
<AppDesignerFolder>Properties</AppDesignerFolder>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<OutputType>Library</OutputType>
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Tribufu.Logging\Tribufu.Logging.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,46 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using Newtonsoft.Json;
using System;
using System.Globalization;
namespace Tribufu.Serialization
{
public class DecimalNullableStringConverter : JsonConverter<decimal?>
{
public override decimal? ReadJson(JsonReader reader, Type objectType, decimal? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.String || reader.TokenType == JsonToken.Integer)
{
string value = reader.Value?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return decimal.Parse(value);
}
throw new JsonSerializationException($"Unexpected token {reader.TokenType} when parsing decimal?.");
}
public override void WriteJson(JsonWriter writer, decimal? value, JsonSerializer serializer)
{
if (value.HasValue)
{
writer.WriteValue(value.Value.ToString(CultureInfo.InvariantCulture));
}
else
{
writer.WriteNull();
}
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using Newtonsoft.Json;
using System;
using System.Globalization;
namespace Tribufu.Serialization
{
public class DecimalStringConverter : JsonConverter<decimal>
{
public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String && decimal.TryParse(reader.Value?.ToString(), out var result))
{
return result;
}
if (reader.TokenType == JsonToken.Integer)
{
return Convert.ToUInt64(reader.Value);
}
throw new JsonSerializationException($"Unexpected token {reader.TokenType} when parsing decimal.");
}
public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString(CultureInfo.InvariantCulture));
}
}
}

View File

@ -0,0 +1 @@
# Tribufu

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Tribufu.Serialization</PackageId>
<Description>Tribufu Serialization Extensions</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup>
<AppDesignerFolder>Properties</AppDesignerFolder>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<OutputType>Library</OutputType>
<TargetFrameworks>netstandard2.0;net45;net5.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,45 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using Newtonsoft.Json;
using System;
namespace Tribufu.Serialization
{
public class ULongNullableStringConverter : JsonConverter<ulong?>
{
public override ulong? ReadJson(JsonReader reader, Type objectType, ulong? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.String || reader.TokenType == JsonToken.Integer)
{
string value = reader.Value?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return ulong.Parse(value);
}
throw new JsonSerializationException($"Unexpected token {reader.TokenType} when parsing ulong?.");
}
public override void WriteJson(JsonWriter writer, ulong? value, JsonSerializer serializer)
{
if (value.HasValue)
{
writer.WriteValue(value.Value.ToString());
}
else
{
writer.WriteNull();
}
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
using Newtonsoft.Json;
using System;
namespace Tribufu.Serialization
{
public class ULongStringConverter : JsonConverter<ulong>
{
public override ulong ReadJson(JsonReader reader, Type objectType, ulong existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String && ulong.TryParse(reader.Value?.ToString(), out var result))
{
return result;
}
if (reader.TokenType == JsonToken.Integer)
{
return Convert.ToUInt64(reader.Value);
}
throw new JsonSerializationException($"Unexpected token {reader.TokenType} when parsing ulong.");
}
public override void WriteJson(JsonWriter writer, ulong value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
}