This commit is contained in:
Tyrrrz
2021-12-08 23:43:35 +02:00
parent 9990387cfa
commit 2feeb21270
132 changed files with 8021 additions and 8154 deletions

View File

@@ -6,65 +6,64 @@ using CliFx.Demo.Utils;
using CliFx.Exceptions;
using CliFx.Infrastructure;
namespace CliFx.Demo.Commands
namespace CliFx.Demo.Commands;
[Command("book add", Description = "Add a book to the library.")]
public partial class BookAddCommand : ICommand
{
[Command("book add", Description = "Add a book to the library.")]
public partial class BookAddCommand : ICommand
private readonly LibraryProvider _libraryProvider;
[CommandParameter(0, Name = "title", Description = "Book title.")]
public string Title { get; init; } = "";
[CommandOption("author", 'a', IsRequired = true, Description = "Book author.")]
public string Author { get; init; } = "";
[CommandOption("published", 'p', Description = "Book publish date.")]
public DateTimeOffset Published { get; init; } = CreateRandomDate();
[CommandOption("isbn", 'n', Description = "Book ISBN.")]
public Isbn Isbn { get; init; } = CreateRandomIsbn();
public BookAddCommand(LibraryProvider libraryProvider)
{
private readonly LibraryProvider _libraryProvider;
[CommandParameter(0, Name = "title", Description = "Book title.")]
public string Title { get; init; } = "";
[CommandOption("author", 'a', IsRequired = true, Description = "Book author.")]
public string Author { get; init; } = "";
[CommandOption("published", 'p', Description = "Book publish date.")]
public DateTimeOffset Published { get; init; } = CreateRandomDate();
[CommandOption("isbn", 'n', Description = "Book ISBN.")]
public Isbn Isbn { get; init; } = CreateRandomIsbn();
public BookAddCommand(LibraryProvider libraryProvider)
{
_libraryProvider = libraryProvider;
}
public ValueTask ExecuteAsync(IConsole console)
{
if (_libraryProvider.TryGetBook(Title) is not null)
throw new CommandException("Book already exists.", 10);
var book = new Book(Title, Author, Published, Isbn);
_libraryProvider.AddBook(book);
console.Output.WriteLine("Book added.");
console.Output.WriteBook(book);
return default;
}
_libraryProvider = libraryProvider;
}
public partial class BookAddCommand
public ValueTask ExecuteAsync(IConsole console)
{
private static readonly Random Random = new();
if (_libraryProvider.TryGetBook(Title) is not null)
throw new CommandException("Book already exists.", 10);
private static DateTimeOffset CreateRandomDate() => new(
Random.Next(1800, 2020),
Random.Next(1, 12),
Random.Next(1, 28),
Random.Next(1, 23),
Random.Next(1, 59),
Random.Next(1, 59),
TimeSpan.Zero
);
var book = new Book(Title, Author, Published, Isbn);
_libraryProvider.AddBook(book);
private static Isbn CreateRandomIsbn() => new(
Random.Next(0, 999),
Random.Next(0, 99),
Random.Next(0, 99999),
Random.Next(0, 99),
Random.Next(0, 9)
);
console.Output.WriteLine("Book added.");
console.Output.WriteBook(book);
return default;
}
}
public partial class BookAddCommand
{
private static readonly Random Random = new();
private static DateTimeOffset CreateRandomDate() => new(
Random.Next(1800, 2020),
Random.Next(1, 12),
Random.Next(1, 28),
Random.Next(1, 23),
Random.Next(1, 59),
Random.Next(1, 59),
TimeSpan.Zero
);
private static Isbn CreateRandomIsbn() => new(
Random.Next(0, 999),
Random.Next(0, 99),
Random.Next(0, 99999),
Random.Next(0, 99),
Random.Next(0, 9)
);
}

View File

@@ -5,31 +5,30 @@ using CliFx.Demo.Utils;
using CliFx.Exceptions;
using CliFx.Infrastructure;
namespace CliFx.Demo.Commands
namespace CliFx.Demo.Commands;
[Command("book", Description = "Retrieve a book from the library.")]
public class BookCommand : ICommand
{
[Command("book", Description = "Retrieve a book from the library.")]
public class BookCommand : ICommand
private readonly LibraryProvider _libraryProvider;
[CommandParameter(0, Name = "title", Description = "Title of the book to retrieve.")]
public string Title { get; init; } = "";
public BookCommand(LibraryProvider libraryProvider)
{
private readonly LibraryProvider _libraryProvider;
_libraryProvider = libraryProvider;
}
[CommandParameter(0, Name = "title", Description = "Title of the book to retrieve.")]
public string Title { get; init; } = "";
public ValueTask ExecuteAsync(IConsole console)
{
var book = _libraryProvider.TryGetBook(Title);
public BookCommand(LibraryProvider libraryProvider)
{
_libraryProvider = libraryProvider;
}
if (book is null)
throw new CommandException("Book not found.", 10);
public ValueTask ExecuteAsync(IConsole console)
{
var book = _libraryProvider.TryGetBook(Title);
console.Output.WriteBook(book);
if (book is null)
throw new CommandException("Book not found.", 10);
console.Output.WriteBook(book);
return default;
}
return default;
}
}

View File

@@ -4,34 +4,33 @@ using CliFx.Demo.Domain;
using CliFx.Demo.Utils;
using CliFx.Infrastructure;
namespace CliFx.Demo.Commands
namespace CliFx.Demo.Commands;
[Command("book list", Description = "List all books in the library.")]
public class BookListCommand : ICommand
{
[Command("book list", Description = "List all books in the library.")]
public class BookListCommand : ICommand
private readonly LibraryProvider _libraryProvider;
public BookListCommand(LibraryProvider libraryProvider)
{
private readonly LibraryProvider _libraryProvider;
_libraryProvider = libraryProvider;
}
public BookListCommand(LibraryProvider libraryProvider)
public ValueTask ExecuteAsync(IConsole console)
{
var library = _libraryProvider.GetLibrary();
for (var i = 0; i < library.Books.Count; i++)
{
_libraryProvider = libraryProvider;
// Add margin
if (i != 0)
console.Output.WriteLine();
// Render book
var book = library.Books[i];
console.Output.WriteBook(book);
}
public ValueTask ExecuteAsync(IConsole console)
{
var library = _libraryProvider.GetLibrary();
for (var i = 0; i < library.Books.Count; i++)
{
// Add margin
if (i != 0)
console.Output.WriteLine();
// Render book
var book = library.Books[i];
console.Output.WriteBook(book);
}
return default;
}
return default;
}
}

View File

@@ -4,33 +4,32 @@ using CliFx.Demo.Domain;
using CliFx.Exceptions;
using CliFx.Infrastructure;
namespace CliFx.Demo.Commands
namespace CliFx.Demo.Commands;
[Command("book remove", Description = "Remove a book from the library.")]
public class BookRemoveCommand : ICommand
{
[Command("book remove", Description = "Remove a book from the library.")]
public class BookRemoveCommand : ICommand
private readonly LibraryProvider _libraryProvider;
[CommandParameter(0, Name = "title", Description = "Title of the book to remove.")]
public string Title { get; init; } = "";
public BookRemoveCommand(LibraryProvider libraryProvider)
{
private readonly LibraryProvider _libraryProvider;
_libraryProvider = libraryProvider;
}
[CommandParameter(0, Name = "title", Description = "Title of the book to remove.")]
public string Title { get; init; } = "";
public ValueTask ExecuteAsync(IConsole console)
{
var book = _libraryProvider.TryGetBook(Title);
public BookRemoveCommand(LibraryProvider libraryProvider)
{
_libraryProvider = libraryProvider;
}
if (book is null)
throw new CommandException("Book not found.", 10);
public ValueTask ExecuteAsync(IConsole console)
{
var book = _libraryProvider.TryGetBook(Title);
_libraryProvider.RemoveBook(book);
if (book is null)
throw new CommandException("Book not found.", 10);
console.Output.WriteLine($"Book {Title} removed.");
_libraryProvider.RemoveBook(book);
console.Output.WriteLine($"Book {Title} removed.");
return default;
}
return default;
}
}

View File

@@ -1,23 +1,22 @@
using System;
namespace CliFx.Demo.Domain
namespace CliFx.Demo.Domain;
public class Book
{
public class Book
public string Title { get; }
public string Author { get; }
public DateTimeOffset Published { get; }
public Isbn Isbn { get; }
public Book(string title, string author, DateTimeOffset published, Isbn isbn)
{
public string Title { get; }
public string Author { get; }
public DateTimeOffset Published { get; }
public Isbn Isbn { get; }
public Book(string title, string author, DateTimeOffset published, Isbn isbn)
{
Title = title;
Author = author;
Published = published;
Isbn = isbn;
}
Title = title;
Author = author;
Published = published;
Isbn = isbn;
}
}

View File

@@ -1,45 +1,44 @@
using System;
namespace CliFx.Demo.Domain
namespace CliFx.Demo.Domain;
public partial class Isbn
{
public partial class Isbn
public int EanPrefix { get; }
public int RegistrationGroup { get; }
public int Registrant { get; }
public int Publication { get; }
public int CheckDigit { get; }
public Isbn(int eanPrefix, int registrationGroup, int registrant, int publication, int checkDigit)
{
public int EanPrefix { get; }
public int RegistrationGroup { get; }
public int Registrant { get; }
public int Publication { get; }
public int CheckDigit { get; }
public Isbn(int eanPrefix, int registrationGroup, int registrant, int publication, int checkDigit)
{
EanPrefix = eanPrefix;
RegistrationGroup = registrationGroup;
Registrant = registrant;
Publication = publication;
CheckDigit = checkDigit;
}
public override string ToString() =>
$"{EanPrefix:000}-{RegistrationGroup:00}-{Registrant:00000}-{Publication:00}-{CheckDigit:0}";
EanPrefix = eanPrefix;
RegistrationGroup = registrationGroup;
Registrant = registrant;
Publication = publication;
CheckDigit = checkDigit;
}
public partial class Isbn
{
public static Isbn Parse(string value, IFormatProvider formatProvider)
{
var components = value.Split('-', 5, StringSplitOptions.RemoveEmptyEntries);
public override string ToString() =>
$"{EanPrefix:000}-{RegistrationGroup:00}-{Registrant:00000}-{Publication:00}-{CheckDigit:0}";
}
return new Isbn(
int.Parse(components[0], formatProvider),
int.Parse(components[1], formatProvider),
int.Parse(components[2], formatProvider),
int.Parse(components[3], formatProvider),
int.Parse(components[4], formatProvider)
);
}
public partial class Isbn
{
public static Isbn Parse(string value, IFormatProvider formatProvider)
{
var components = value.Split('-', 5, StringSplitOptions.RemoveEmptyEntries);
return new Isbn(
int.Parse(components[0], formatProvider),
int.Parse(components[1], formatProvider),
int.Parse(components[2], formatProvider),
int.Parse(components[3], formatProvider),
int.Parse(components[4], formatProvider)
);
}
}

View File

@@ -2,35 +2,34 @@
using System.Collections.Generic;
using System.Linq;
namespace CliFx.Demo.Domain
namespace CliFx.Demo.Domain;
public partial class Library
{
public partial class Library
public IReadOnlyList<Book> Books { get; }
public Library(IReadOnlyList<Book> books)
{
public IReadOnlyList<Book> Books { get; }
public Library(IReadOnlyList<Book> books)
{
Books = books;
}
public Library WithBook(Book book)
{
var books = Books.ToList();
books.Add(book);
return new Library(books);
}
public Library WithoutBook(Book book)
{
var books = Books.Where(b => b != book).ToArray();
return new Library(books);
}
Books = books;
}
public partial class Library
public Library WithBook(Book book)
{
public static Library Empty { get; } = new(Array.Empty<Book>());
var books = Books.ToList();
books.Add(book);
return new Library(books);
}
public Library WithoutBook(Book book)
{
var books = Books.Where(b => b != book).ToArray();
return new Library(books);
}
}
public partial class Library
{
public static Library Empty { get; } = new(Array.Empty<Book>());
}

View File

@@ -2,40 +2,39 @@
using System.Linq;
using Newtonsoft.Json;
namespace CliFx.Demo.Domain
namespace CliFx.Demo.Domain;
public class LibraryProvider
{
public class LibraryProvider
private static string StorageFilePath { get; } = Path.Combine(Directory.GetCurrentDirectory(), "Library.json");
private void StoreLibrary(Library library)
{
private static string StorageFilePath { get; } = Path.Combine(Directory.GetCurrentDirectory(), "Library.json");
var data = JsonConvert.SerializeObject(library);
File.WriteAllText(StorageFilePath, data);
}
private void StoreLibrary(Library library)
{
var data = JsonConvert.SerializeObject(library);
File.WriteAllText(StorageFilePath, data);
}
public Library GetLibrary()
{
if (!File.Exists(StorageFilePath))
return Library.Empty;
public Library GetLibrary()
{
if (!File.Exists(StorageFilePath))
return Library.Empty;
var data = File.ReadAllText(StorageFilePath);
var data = File.ReadAllText(StorageFilePath);
return JsonConvert.DeserializeObject<Library>(data) ?? Library.Empty;
}
return JsonConvert.DeserializeObject<Library>(data) ?? Library.Empty;
}
public Book? TryGetBook(string title) => GetLibrary().Books.FirstOrDefault(b => b.Title == title);
public Book? TryGetBook(string title) => GetLibrary().Books.FirstOrDefault(b => b.Title == title);
public void AddBook(Book book)
{
var updatedLibrary = GetLibrary().WithBook(book);
StoreLibrary(updatedLibrary);
}
public void AddBook(Book book)
{
var updatedLibrary = GetLibrary().WithBook(book);
StoreLibrary(updatedLibrary);
}
public void RemoveBook(Book book)
{
var updatedLibrary = GetLibrary().WithoutBook(book);
StoreLibrary(updatedLibrary);
}
public void RemoveBook(Book book)
{
var updatedLibrary = GetLibrary().WithoutBook(book);
StoreLibrary(updatedLibrary);
}
}

View File

@@ -4,33 +4,32 @@ using CliFx.Demo.Commands;
using CliFx.Demo.Domain;
using Microsoft.Extensions.DependencyInjection;
namespace CliFx.Demo
namespace CliFx.Demo;
public static class Program
{
public static class Program
private static IServiceProvider GetServiceProvider()
{
private static IServiceProvider GetServiceProvider()
{
// We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands
var services = new ServiceCollection();
// We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands
var services = new ServiceCollection();
// Register services
services.AddSingleton<LibraryProvider>();
// Register services
services.AddSingleton<LibraryProvider>();
// Register commands
services.AddTransient<BookCommand>();
services.AddTransient<BookAddCommand>();
services.AddTransient<BookRemoveCommand>();
services.AddTransient<BookListCommand>();
// Register commands
services.AddTransient<BookCommand>();
services.AddTransient<BookAddCommand>();
services.AddTransient<BookRemoveCommand>();
services.AddTransient<BookListCommand>();
return services.BuildServiceProvider();
}
public static async Task<int> Main() =>
await new CliApplicationBuilder()
.SetDescription("Demo application showcasing CliFx features.")
.AddCommandsFromThisAssembly()
.UseTypeActivator(GetServiceProvider().GetRequiredService)
.Build()
.RunAsync();
return services.BuildServiceProvider();
}
public static async Task<int> Main() =>
await new CliApplicationBuilder()
.SetDescription("Demo application showcasing CliFx features.")
.AddCommandsFromThisAssembly()
.UseTypeActivator(GetServiceProvider().GetRequiredService)
.Build()
.RunAsync();
}

View File

@@ -2,36 +2,35 @@
using CliFx.Demo.Domain;
using CliFx.Infrastructure;
namespace CliFx.Demo.Utils
namespace CliFx.Demo.Utils;
internal static class ConsoleExtensions
{
internal static class ConsoleExtensions
public static void WriteBook(this ConsoleWriter writer, Book book)
{
public static void WriteBook(this ConsoleWriter writer, Book book)
{
// Title
using (writer.Console.WithForegroundColor(ConsoleColor.White))
writer.WriteLine(book.Title);
// Title
using (writer.Console.WithForegroundColor(ConsoleColor.White))
writer.WriteLine(book.Title);
// Author
writer.Write(" ");
writer.Write("Author: ");
// Author
writer.Write(" ");
writer.Write("Author: ");
using (writer.Console.WithForegroundColor(ConsoleColor.White))
writer.WriteLine(book.Author);
using (writer.Console.WithForegroundColor(ConsoleColor.White))
writer.WriteLine(book.Author);
// Published
writer.Write(" ");
writer.Write("Published: ");
// Published
writer.Write(" ");
writer.Write("Published: ");
using (writer.Console.WithForegroundColor(ConsoleColor.White))
writer.WriteLine($"{book.Published:d}");
using (writer.Console.WithForegroundColor(ConsoleColor.White))
writer.WriteLine($"{book.Published:d}");
// ISBN
writer.Write(" ");
writer.Write("ISBN: ");
// ISBN
writer.Write(" ");
writer.Write("ISBN: ");
using (writer.Console.WithForegroundColor(ConsoleColor.White))
writer.WriteLine(book.Isbn);
}
using (writer.Console.WithForegroundColor(ConsoleColor.White))
writer.WriteLine(book.Isbn);
}
}