diff options
| author | Kostya <mail@sartin.in> | 2026-08-14 18:49:26 +0000 |
|---|---|---|
| committer | Kostya <mail@sartin.in> | 2026-08-14 18:49:26 +0000 |
| commit | 12611a5f0116351ab9114f5403222aead7a8329f (patch) | |
| tree | f403cb522e4ea63ac2e919d7e72d6afa83eea878 /ui-console/src/main/java | |
| parent | ec716e23ea8d827d4401ced8f34d533e30209d55 (diff) | |
| download | guess-market-12611a5f0116351ab9114f5403222aead7a8329f.tar.gz guess-market-12611a5f0116351ab9114f5403222aead7a8329f.tar.xz guess-market-12611a5f0116351ab9114f5403222aead7a8329f.zip | |
Implement in-memory repositories, XML load validation, and menu exit/list commands
- Add generic Repository<T> interface; EventRepository/UserRepository extend it
- Implement InMemoryEventRepository/InMemoryUserRepository backed by ArrayList
- Rename XMLLoader to XMLLoaderV1, add LoadValidator and mapper/v2 package
- Add OrderBookTradingMechanism
- Add User(String, int) convenience constructor
- Add ListEventsMenuCommand and ExitMenuCommand, isExit() on MenuCommand
- Menu now sizes selection range by max command index and exits on isExit()
- LoadFileMenuCommand reports validation issues on failed loads
- Untrack test-data/ and config.properties from .gitignore
Diffstat (limited to 'ui-console/src/main/java')
6 files changed, 111 insertions, 5 deletions
diff --git a/ui-console/src/main/java/market/guess/ui/console/App.java b/ui-console/src/main/java/market/guess/ui/console/App.java index 2bff741..34638ac 100644 --- a/ui-console/src/main/java/market/guess/ui/console/App.java +++ b/ui-console/src/main/java/market/guess/ui/console/App.java | |||
| @@ -4,12 +4,15 @@ import market.guess.api.GuessMarketContext; | |||
| 4 | import market.guess.service.LocalGuessMarketContext; | 4 | import market.guess.service.LocalGuessMarketContext; |
| 5 | import market.guess.service.TradingOperation; | 5 | import market.guess.service.TradingOperation; |
| 6 | import market.guess.service.TradingOperationImpl; | 6 | import market.guess.service.TradingOperationImpl; |
| 7 | import market.guess.service.infrastructure.mapper.v1.EventMapperV1; | ||
| 7 | import market.guess.service.infrastructure.provider.Loader; | 8 | import market.guess.service.infrastructure.provider.Loader; |
| 8 | import market.guess.service.infrastructure.provider.v1.XMLLoader; | 9 | import market.guess.service.infrastructure.provider.v1.XMLLoaderV1; |
| 9 | import market.guess.service.repository.EventRepository; | 10 | import market.guess.service.repository.EventRepository; |
| 10 | import market.guess.service.repository.InMemoryEventRepository; | 11 | import market.guess.service.repository.InMemoryEventRepository; |
| 11 | import market.guess.service.repository.InMemoryUserRepository; | 12 | import market.guess.service.repository.InMemoryUserRepository; |
| 12 | import market.guess.service.repository.UserRepository; | 13 | import market.guess.service.repository.UserRepository; |
| 14 | import market.guess.ui.console.command.ExitMenuCommand; | ||
| 15 | import market.guess.ui.console.command.ListEventsMenuCommand; | ||
| 13 | import market.guess.ui.console.command.LoadFileMenuCommand; | 16 | import market.guess.ui.console.command.LoadFileMenuCommand; |
| 14 | import market.guess.ui.console.io.ConsoleInputProvider; | 17 | import market.guess.ui.console.io.ConsoleInputProvider; |
| 15 | import market.guess.ui.console.io.ConsoleOutputProvider; | 18 | import market.guess.ui.console.io.ConsoleOutputProvider; |
| @@ -27,10 +30,13 @@ public class App { | |||
| 27 | pico.addComponent(UserRepository.class, InMemoryUserRepository.class); | 30 | pico.addComponent(UserRepository.class, InMemoryUserRepository.class); |
| 28 | 31 | ||
| 29 | pico.addComponent(TradingOperation.class, TradingOperationImpl.class); | 32 | pico.addComponent(TradingOperation.class, TradingOperationImpl.class); |
| 30 | pico.addComponent(Loader.class, XMLLoader.class); | 33 | pico.addComponent(EventMapperV1.class); |
| 34 | pico.addComponent(Loader.class, XMLLoaderV1.class); | ||
| 31 | pico.addComponent(GuessMarketContext.class, LocalGuessMarketContext.class); | 35 | pico.addComponent(GuessMarketContext.class, LocalGuessMarketContext.class); |
| 32 | 36 | ||
| 33 | pico.addComponent(LoadFileMenuCommand.class); | 37 | pico.addComponent(LoadFileMenuCommand.class); |
| 38 | pico.addComponent(ListEventsMenuCommand.class); | ||
| 39 | pico.addComponent(ExitMenuCommand.class); | ||
| 34 | 40 | ||
| 35 | pico.addComponent(InputProvider.class, ConsoleInputProvider.class); | 41 | pico.addComponent(InputProvider.class, ConsoleInputProvider.class); |
| 36 | pico.addComponent(OutputProvider.class, ConsoleOutputProvider.class); | 42 | pico.addComponent(OutputProvider.class, ConsoleOutputProvider.class); |
diff --git a/ui-console/src/main/java/market/guess/ui/console/Menu.java b/ui-console/src/main/java/market/guess/ui/console/Menu.java index 0521e5f..c1762dc 100644 --- a/ui-console/src/main/java/market/guess/ui/console/Menu.java +++ b/ui-console/src/main/java/market/guess/ui/console/Menu.java | |||
| @@ -19,12 +19,12 @@ public final class Menu implements Startable { | |||
| 19 | 19 | ||
| 20 | @Override | 20 | @Override |
| 21 | public void start() { | 21 | public void start() { |
| 22 | var max = commands.stream().mapToInt(MenuCommand::getIndex).max().orElse(0); | ||
| 22 | out.splashScreen(); | 23 | out.splashScreen(); |
| 23 | |||
| 24 | while (true) { | 24 | while (true) { |
| 25 | printMenu(); | 25 | printMenu(); |
| 26 | 26 | ||
| 27 | var selection = in.readInt("Choose a command: ", 1, commands.size()); | 27 | var selection = in.readInt("Choose a command: ", 1, max); |
| 28 | var commandOptional = commands.stream().filter(c -> c.getIndex() == selection).findFirst(); | 28 | var commandOptional = commands.stream().filter(c -> c.getIndex() == selection).findFirst(); |
| 29 | 29 | ||
| 30 | if (commandOptional.isEmpty()) { | 30 | if (commandOptional.isEmpty()) { |
| @@ -34,6 +34,8 @@ public final class Menu implements Startable { | |||
| 34 | var command = commandOptional.get(); | 34 | var command = commandOptional.get(); |
| 35 | 35 | ||
| 36 | command.execute(); | 36 | command.execute(); |
| 37 | |||
| 38 | if (command.isExit()) return; | ||
| 37 | } | 39 | } |
| 38 | } | 40 | } |
| 39 | 41 | ||
| @@ -45,7 +47,7 @@ public final class Menu implements Startable { | |||
| 45 | private void printMenu() { | 47 | private void printMenu() { |
| 46 | out.newLine(); | 48 | out.newLine(); |
| 47 | for (var command : commands) { | 49 | for (var command : commands) { |
| 48 | out.println(" %d -- %s".formatted(command.getIndex(), command.getName())); | 50 | out.println(" [%d]->> %s".formatted(command.getIndex(), command.getName())); |
| 49 | } | 51 | } |
| 50 | } | 52 | } |
| 51 | } | 53 | } |
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/ExitMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/ExitMenuCommand.java new file mode 100644 index 0000000..372c686 --- /dev/null +++ b/ui-console/src/main/java/market/guess/ui/console/command/ExitMenuCommand.java | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | package market.guess.ui.console.command; | ||
| 2 | |||
| 3 | import market.guess.ui.console.io.OutputProvider; | ||
| 4 | |||
| 5 | public final class ExitMenuCommand implements MenuCommand { | ||
| 6 | private final OutputProvider out; | ||
| 7 | |||
| 8 | public ExitMenuCommand(OutputProvider out) { | ||
| 9 | super(); | ||
| 10 | this.out = out; | ||
| 11 | } | ||
| 12 | |||
| 13 | @Override | ||
| 14 | public int getIndex() { | ||
| 15 | return 6; | ||
| 16 | } | ||
| 17 | |||
| 18 | @Override | ||
| 19 | public String getName() { | ||
| 20 | return "Exit"; | ||
| 21 | } | ||
| 22 | |||
| 23 | @Override | ||
| 24 | public boolean allowed() { | ||
| 25 | return true; | ||
| 26 | } | ||
| 27 | |||
| 28 | @Override | ||
| 29 | public boolean isExit() { | ||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | @Override | ||
| 34 | public void execute() { | ||
| 35 | out.println("Goodbye."); | ||
| 36 | } | ||
| 37 | } | ||
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/ListEventsMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/ListEventsMenuCommand.java new file mode 100644 index 0000000..6e6ace9 --- /dev/null +++ b/ui-console/src/main/java/market/guess/ui/console/command/ListEventsMenuCommand.java | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | package market.guess.ui.console.command; | ||
| 2 | |||
| 3 | import market.guess.api.CommissionTiming; | ||
| 4 | import market.guess.api.GuessMarketContext; | ||
| 5 | import market.guess.ui.console.io.OutputProvider; | ||
| 6 | |||
| 7 | public final class ListEventsMenuCommand implements MenuCommand { | ||
| 8 | private final GuessMarketContext context; | ||
| 9 | private final OutputProvider out; | ||
| 10 | |||
| 11 | public ListEventsMenuCommand(GuessMarketContext context, OutputProvider out) { | ||
| 12 | super(); | ||
| 13 | this.context = context; | ||
| 14 | this.out = out; | ||
| 15 | } | ||
| 16 | |||
| 17 | @Override | ||
| 18 | public int getIndex() { | ||
| 19 | return 2; | ||
| 20 | } | ||
| 21 | |||
| 22 | @Override | ||
| 23 | public String getName() { | ||
| 24 | return "List events"; | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | ||
| 28 | public boolean allowed() { | ||
| 29 | return !context.listEvents().isEmpty(); | ||
| 30 | } | ||
| 31 | |||
| 32 | @Override | ||
| 33 | public void execute() { | ||
| 34 | var events = context.listEvents(); | ||
| 35 | |||
| 36 | for (var event : events) { | ||
| 37 | out.newLine(); | ||
| 38 | out.println(" %s".formatted(event.name())); | ||
| 39 | out.println(" %s".formatted(event.description())); | ||
| 40 | out.println( | ||
| 41 | " Commission: %d%% (%s)" | ||
| 42 | .formatted(event.commissionPercent(), timing(event.commissionTiming()))); | ||
| 43 | out.println(" Options: " + String.join(" | ", event.optionNames())); | ||
| 44 | out.println(" Status: " + event.status()); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | private static String timing(CommissionTiming timing) { | ||
| 49 | return timing == CommissionTiming.ON_CLOSE ? "charged on close" : "charged on purchase"; | ||
| 50 | } | ||
| 51 | } | ||
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java index 5a91fe0..4a56483 100644 --- a/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java +++ b/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java | |||
| @@ -40,5 +40,11 @@ public final class LoadFileMenuCommand implements MenuCommand { | |||
| 40 | out.println("Loaded %d event(s) from %s.".formatted(result.eventsLoaded(), result.source())); | 40 | out.println("Loaded %d event(s) from %s.".formatted(result.eventsLoaded(), result.source())); |
| 41 | return; | 41 | return; |
| 42 | } | 42 | } |
| 43 | |||
| 44 | out.println( | ||
| 45 | "The file was not loaded. %d problem(s) were found: ".formatted(result.issues().size())); | ||
| 46 | for (var issue : result.issues()) { | ||
| 47 | out.println(" - %s: %s".formatted(issue.location(), issue.humanizedMessage())); | ||
| 48 | } | ||
| 43 | } | 49 | } |
| 44 | } | 50 | } |
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java index 8a28dfe..50acdac 100644 --- a/ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java +++ b/ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java | |||
| @@ -8,4 +8,8 @@ public interface MenuCommand { | |||
| 8 | boolean allowed(); | 8 | boolean allowed(); |
| 9 | 9 | ||
| 10 | void execute(); | 10 | void execute(); |
| 11 | |||
| 12 | default boolean isExit() { | ||
| 13 | return false; | ||
| 14 | } | ||
| 11 | } | 15 | } |