summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKostya <mail@sartin.in>2026-08-14 18:49:26 +0000
committerKostya <mail@sartin.in>2026-08-14 18:49:26 +0000
commit12611a5f0116351ab9114f5403222aead7a8329f (patch)
treef403cb522e4ea63ac2e919d7e72d6afa83eea878
parentec716e23ea8d827d4401ced8f34d533e30209d55 (diff)
downloadguess-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
-rw-r--r--.gitignore3
-rw-r--r--service/src/main/java/market/guess/service/LocalGuessMarketContext.java11
-rw-r--r--service/src/main/java/market/guess/service/domain/Trade.java4
-rw-r--r--service/src/main/java/market/guess/service/domain/User.java4
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java3
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java66
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java22
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java (renamed from service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoader.java)33
-rw-r--r--service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java40
-rw-r--r--service/src/main/java/market/guess/service/repository/EventRepository.java9
-rw-r--r--service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java20
-rw-r--r--service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java22
-rw-r--r--service/src/main/java/market/guess/service/repository/Repository.java12
-rw-r--r--service/src/main/java/market/guess/service/repository/UserRepository.java8
-rw-r--r--test-data/ex-1/multiple.xml47
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/App.java10
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/Menu.java8
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/command/ExitMenuCommand.java37
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/command/ListEventsMenuCommand.java51
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java6
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java4
21 files changed, 359 insertions, 61 deletions
diff --git a/.gitignore b/.gitignore
index 8e3b8c9..7a9d622 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,12 +2,9 @@
2bin/ 2bin/
3stage/ 3stage/
4dist/ 4dist/
5test-data/
6*.class 5*.class
7 6
8# Ignore OS-specific files 7# Ignore OS-specific files
9.DS_Store 8.DS_Store
10Thumbs.db 9Thumbs.db
11 10
12config.properties
13
diff --git a/service/src/main/java/market/guess/service/LocalGuessMarketContext.java b/service/src/main/java/market/guess/service/LocalGuessMarketContext.java
index 16ee1b8..19c7297 100644
--- a/service/src/main/java/market/guess/service/LocalGuessMarketContext.java
+++ b/service/src/main/java/market/guess/service/LocalGuessMarketContext.java
@@ -41,7 +41,7 @@ public final class LocalGuessMarketContext implements GuessMarketContext {
41 41
42 @Override 42 @Override
43 public List<EventSummaryDTO> listEvents() { 43 public List<EventSummaryDTO> listEvents() {
44 return eventRepository.getAllEvents().stream().map(Event::toEventSummary).toList(); 44 return eventRepository.getAll().stream().map(Event::toEventSummary).toList();
45 } 45 }
46 46
47 @Override 47 @Override
@@ -58,9 +58,14 @@ public final class LocalGuessMarketContext implements GuessMarketContext {
58 public PurchaseReceiptDTO buyShares( 58 public PurchaseReceiptDTO buyShares(
59 String userName, String eventKey, String optionKey, int quantity) 59 String userName, String eventKey, String optionKey, int quantity)
60 throws GuessMarketException { 60 throws GuessMarketException {
61 var event = eventRepository.getEvent(eventKey); 61 var optional = eventRepository.get(eventKey);
62
63 var userOptional = userRepository.get(userName);
64 if (optional.isEmpty() || userOptional.isEmpty()) throw new GuessMarketException();
65
66 var event = optional.get();
67 var user = userOptional.get();
62 var option = event.getOption(optionKey); 68 var option = event.getOption(optionKey);
63 var user = userRepository.getUser(userName);
64 var trade = tradingOperation.buy(event, user, option, quantity); 69 var trade = tradingOperation.buy(event, user, option, quantity);
65 return new PurchaseReceiptDTO(); 70 return new PurchaseReceiptDTO();
66 } 71 }
diff --git a/service/src/main/java/market/guess/service/domain/Trade.java b/service/src/main/java/market/guess/service/domain/Trade.java
index cb6ed93..0c07749 100644
--- a/service/src/main/java/market/guess/service/domain/Trade.java
+++ b/service/src/main/java/market/guess/service/domain/Trade.java
@@ -4,12 +4,12 @@ import java.math.BigDecimal;
4import java.time.Instant; 4import java.time.Instant;
5 5
6public record Trade( 6public record Trade(
7 long seq, 7 int id,
8 Instant at, 8 Instant at,
9 String userName, 9 String userName,
10 String optionKey, 10 String optionKey,
11 String optionName, 11 String optionName,
12 long quantity, 12 int uantity,
13 BigDecimal sharesCost, 13 BigDecimal sharesCost,
14 BigDecimal commission) { 14 BigDecimal commission) {
15 15
diff --git a/service/src/main/java/market/guess/service/domain/User.java b/service/src/main/java/market/guess/service/domain/User.java
index af9ac78..b2084b4 100644
--- a/service/src/main/java/market/guess/service/domain/User.java
+++ b/service/src/main/java/market/guess/service/domain/User.java
@@ -6,6 +6,10 @@ public final class User {
6 private final String name; 6 private final String name;
7 private final Account account; 7 private final Account account;
8 8
9 public User(String name, int initialBalance) {
10 this(name, BigDecimalOptions.toMoney(initialBalance));
11 }
12
9 public User(String name, BigDecimal initialBalance) { 13 public User(String name, BigDecimal initialBalance) {
10 this.name = name; 14 this.name = name;
11 this.account = new Account(name, initialBalance); 15 this.account = new Account(name, initialBalance);
diff --git a/service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java b/service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java
index 0e8a0e0..bc5ddb0 100644
--- a/service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java
+++ b/service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java
@@ -4,7 +4,8 @@ import com.google.gson.*;
4import java.lang.reflect.Type; 4import java.lang.reflect.Type;
5import java.time.Instant; 5import java.time.Instant;
6 6
7public class InstantTypeAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> { 7public final class InstantTypeAdapter
8 implements JsonSerializer<Instant>, JsonDeserializer<Instant> {
8 @Override 9 @Override
9 public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { 10 public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) {
10 return new JsonPrimitive(src.toString()); 11 return new JsonPrimitive(src.toString());
diff --git a/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java b/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java
new file mode 100644
index 0000000..687ec33
--- /dev/null
+++ b/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java
@@ -0,0 +1,66 @@
1package market.guess.service.infrastructure.mapper.v2;
2
3import java.util.ArrayList;
4import java.util.List;
5import market.guess.api.CommissionTiming;
6import market.guess.api.EventStatus;
7import market.guess.service.domain.Event;
8import market.guess.service.domain.Option;
9import market.guess.service.infrastructure.mapper.Mapper;
10import market.guess.service.mechanism.LmsrTradingMechanism;
11import market.guess.service.mechanism.OrderBookTradingMechanism;
12import market.guess.service.mechanism.TradingMechanism;
13import market.guess.service.model.v2.GMEvent;
14import market.guess.service.model.v2.GMLMSR;
15import market.guess.service.model.v2.GMOrderBook;
16
17public final class EventMapperV2 implements Mapper<GMEvent, Event> {
18
19 @Override
20 public Event toDomain(GMEvent source) {
21 return new Event(
22 getEventKey(source),
23 source.getId(),
24 source.getName(),
25 source.getDescription(),
26 source.getCommission().getValue(),
27 getTiming(source.getCommission().getType()),
28 getMechanism(source),
29 EventStatus.NOT_STARTED,
30 getOptions(source),
31 "");
32 }
33
34 private static String getEventKey(GMEvent event) {
35 return String.valueOf(event.getId());
36 }
37
38 private static CommissionTiming getTiming(String type) throws IllegalArgumentException {
39 return switch (type) {
40 case "on-close" -> CommissionTiming.ON_CLOSE;
41 case "on-purchase" -> CommissionTiming.ON_PURCHASE;
42 default -> throw new IllegalArgumentException("Unknown commission type: " + type);
43 };
44 }
45
46 private static TradingMechanism getMechanism(GMEvent source) {
47 if (source.getGMMethod().getGMLMSR() instanceof GMLMSR lmsr) {
48 return new LmsrTradingMechanism(lmsr.getB());
49 }
50 if (source.getGMMethod().getGMOrderBook() instanceof GMOrderBook orderBook) {
51 return new OrderBookTradingMechanism(
52 orderBook.getAllowMint().equalsIgnoreCase("true"),
53 orderBook.getInitial(),
54 orderBook.getD());
55 }
56 throw new IllegalArgumentException();
57 }
58
59 private static List<Option> getOptions(GMEvent x) {
60 var ret = new ArrayList<Option>(x.getGMOptions().getGMOption().size());
61 for (var i = 0; i < x.getGMOptions().getGMOption().size(); i++) {
62 ret.add(new Option(getEventKey(x) + ":" + i, x.getGMOptions().getGMOption().get(i)));
63 }
64 return ret;
65 }
66}
diff --git a/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java b/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java
new file mode 100644
index 0000000..c1c3895
--- /dev/null
+++ b/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java
@@ -0,0 +1,22 @@
1package market.guess.service.infrastructure.provider;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6import market.guess.api.ProblemDTO;
7
8public final class LoadValidator {
9 private final List<ProblemDTO> problems = new ArrayList<>();
10
11 public void append(String code, String humanizedMessage, String location) {
12 problems.add(new ProblemDTO(code, humanizedMessage, location));
13 }
14
15 public boolean isValid() {
16 return problems.isEmpty();
17 }
18
19 public List<ProblemDTO> getIssues() {
20 return Collections.unmodifiableList(problems);
21 }
22}
diff --git a/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoader.java b/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java
index 9333350..7af22ea 100644
--- a/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoader.java
+++ b/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java
@@ -4,28 +4,24 @@ import jakarta.xml.bind.JAXBContext;
4import jakarta.xml.bind.JAXBException; 4import jakarta.xml.bind.JAXBException;
5import java.nio.file.Files; 5import java.nio.file.Files;
6import java.nio.file.Path; 6import java.nio.file.Path;
7import java.util.List;
8import market.guess.api.LoadResultDTO; 7import market.guess.api.LoadResultDTO;
9import market.guess.service.domain.BigDecimalOptions; 8import market.guess.service.domain.User;
10import market.guess.service.domain.Event; 9import market.guess.service.infrastructure.mapper.v1.EventMapperV1;
11import market.guess.service.infrastructure.mapper.Mapper; 10import market.guess.service.infrastructure.provider.LoadValidator;
12import market.guess.service.infrastructure.provider.Loader; 11import market.guess.service.infrastructure.provider.Loader;
13import market.guess.service.model.v1.GMEvent;
14import market.guess.service.model.v1.GuessMarket; 12import market.guess.service.model.v1.GuessMarket;
15import market.guess.service.repository.EventRepository; 13import market.guess.service.repository.EventRepository;
16import market.guess.service.repository.UserRepository; 14import market.guess.service.repository.UserRepository;
17 15
18public final class XMLLoader implements Loader { 16public final class XMLLoaderV1 implements Loader {
19 private final JAXBContext context; 17 private final JAXBContext context;
20 18
21 private final EventRepository eventRepository; 19 private final EventRepository eventRepository;
22 private final UserRepository userRepository; 20 private final UserRepository userRepository;
23 private final Mapper<GMEvent, Event> mapper; 21 private final EventMapperV1 mapper;
24 22
25 public XMLLoader( 23 public XMLLoaderV1(
26 EventRepository eventRepository, 24 EventRepository eventRepository, UserRepository userRepository, EventMapperV1 mapper) {
27 UserRepository userRepository,
28 Mapper<GMEvent, Event> mapper) {
29 super(); 25 super();
30 this.eventRepository = eventRepository; 26 this.eventRepository = eventRepository;
31 this.userRepository = userRepository; 27 this.userRepository = userRepository;
@@ -39,22 +35,27 @@ public final class XMLLoader implements Loader {
39 35
40 @Override 36 @Override
41 public LoadResultDTO load(Path path) { 37 public LoadResultDTO load(Path path) {
38 var validator = new LoadValidator();
39 if (path.getFileName() == null
40 || !path.getFileName().toString().toLowerCase().endsWith(".xml")) {
41 validator.append("NOT_XML", "The file must be an XML.", path.toString());
42 }
42 43
43 try (var stream = Files.newInputStream(path)) { 44 try (var stream = Files.newInputStream(path)) {
44 45
45 var doc = (GuessMarket) context.createUnmarshaller().unmarshal(stream); 46 var doc = (GuessMarket) context.createUnmarshaller().unmarshal(stream);
46 47
47 for (var event : doc.getGMEvents().getGMEvent()) { 48 for (var event : doc.getGMEvents().getGMEvent()) {
48 eventRepository.addEvent(mapper.toDomain(event)); 49 eventRepository.add(mapper.toDomain(event));
49 } 50 }
50 51
51 userRepository.addUser("Tester", BigDecimalOptions.toMoney(9_999_999)); 52 userRepository.add(new User("Tester", 9_999_999));
52 53
53 return LoadResultDTO.success("udk", 1); 54 return LoadResultDTO.success(path.toString(), eventRepository.getAll().size());
54 } catch (Exception e) { 55 } catch (Exception e) {
55 56 validator.append("UNREADABLE", "The file could not be read.", path.toString());
56 } 57 }
57 58
58 return LoadResultDTO.failure(path.toFile().getName(), List.of()); 59 return LoadResultDTO.failure(path.toString(), validator.getIssues());
59 } 60 }
60} 61}
diff --git a/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java b/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java
new file mode 100644
index 0000000..62feb02
--- /dev/null
+++ b/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java
@@ -0,0 +1,40 @@
1package market.guess.service.mechanism;
2
3import java.math.BigDecimal;
4import market.guess.api.MechanismType;
5
6public final class OrderBookTradingMechanism implements TradingMechanism {
7 private final boolean allowMint;
8 private final int initial;
9 private final int d;
10
11 public OrderBookTradingMechanism(boolean allowMint, int initial, int d) {
12 super();
13 this.allowMint = allowMint;
14 this.initial = initial;
15 this.d = d;
16 }
17
18 @Override
19 public MechanismType type() {
20 return MechanismType.ORDER_BOOK;
21 }
22
23 @Override
24 public BigDecimal openingCost(int optionCount) {
25 // TODO Auto-generated method stub
26 throw new UnsupportedOperationException("Unimplemented method 'openingCost'");
27 }
28
29 @Override
30 public BigDecimal costOfBuying(long[] q, int optionIndex, long quantity) {
31 // TODO Auto-generated method stub
32 throw new UnsupportedOperationException("Unimplemented method 'costOfBuying'");
33 }
34
35 @Override
36 public double[] prices(long[] q) {
37 // TODO Auto-generated method stub
38 throw new UnsupportedOperationException("Unimplemented method 'prices'");
39 }
40}
diff --git a/service/src/main/java/market/guess/service/repository/EventRepository.java b/service/src/main/java/market/guess/service/repository/EventRepository.java
index 94999cb..afc5371 100644
--- a/service/src/main/java/market/guess/service/repository/EventRepository.java
+++ b/service/src/main/java/market/guess/service/repository/EventRepository.java
@@ -1,12 +1,5 @@
1package market.guess.service.repository; 1package market.guess.service.repository;
2 2
3import java.util.List;
4import market.guess.service.domain.Event; 3import market.guess.service.domain.Event;
5 4
6public interface EventRepository { 5public interface EventRepository extends Repository<Event> {}
7 Event getEvent(String id);
8
9 List<Event> getAllEvents();
10
11 void addEvent(Event event);
12}
diff --git a/service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java b/service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java
index 81eba74..eb7763e 100644
--- a/service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java
+++ b/service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java
@@ -1,25 +1,27 @@
1package market.guess.service.repository; 1package market.guess.service.repository;
2 2
3import java.util.ArrayList;
4import java.util.Collections;
3import java.util.List; 5import java.util.List;
6import java.util.Optional;
4import market.guess.service.domain.Event; 7import market.guess.service.domain.Event;
5 8
6public final class InMemoryEventRepository implements EventRepository { 9public final class InMemoryEventRepository implements EventRepository {
10 private final List<Event> events = new ArrayList<>();
7 11
8 @Override 12 @Override
9 public Event getEvent(String id) { 13 public Event add(Event element) {
10 // TODO Auto-generated method stub 14 events.add(element);
11 throw new UnsupportedOperationException("Unimplemented method 'getEvent'"); 15 return element;
12 } 16 }
13 17
14 @Override 18 @Override
15 public List<Event> getAllEvents() { 19 public Optional<Event> get(String id) {
16 // TODO Auto-generated method stub 20 return events.stream().filter(event -> event.getEventKey().equalsIgnoreCase(id)).findFirst();
17 throw new UnsupportedOperationException("Unimplemented method 'getAllEvents'");
18 } 21 }
19 22
20 @Override 23 @Override
21 public void addEvent(Event event) { 24 public List<Event> getAll() {
22 // TODO Auto-generated method stub 25 return Collections.unmodifiableList(events);
23 throw new UnsupportedOperationException("Unimplemented method 'addEvent'");
24 } 26 }
25} 27}
diff --git a/service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java b/service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java
index 951dea8..0b5cfe7 100644
--- a/service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java
+++ b/service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java
@@ -1,19 +1,27 @@
1package market.guess.service.repository; 1package market.guess.service.repository;
2 2
3import java.math.BigDecimal; 3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6import java.util.Optional;
4import market.guess.service.domain.User; 7import market.guess.service.domain.User;
5 8
6public final class InMemoryUserRepository implements UserRepository { 9public final class InMemoryUserRepository implements UserRepository {
10 private final List<User> users = new ArrayList<>();
7 11
8 @Override 12 @Override
9 public User getUser(String name) { 13 public User add(User element) {
10 // TODO Auto-generated method stub 14 users.add(element);
11 throw new UnsupportedOperationException("Unimplemented method 'getUser'"); 15 return element;
12 } 16 }
13 17
14 @Override 18 @Override
15 public void addUser(String name, BigDecimal initialBalance) { 19 public Optional<User> get(String id) {
16 // TODO Auto-generated method stub 20 return users.stream().filter(u -> u.getName().equalsIgnoreCase(id)).findFirst();
17 throw new UnsupportedOperationException("Unimplemented method 'addUser'"); 21 }
22
23 @Override
24 public List<User> getAll() {
25 return Collections.unmodifiableList(users);
18 } 26 }
19} 27}
diff --git a/service/src/main/java/market/guess/service/repository/Repository.java b/service/src/main/java/market/guess/service/repository/Repository.java
new file mode 100644
index 0000000..8ea1817
--- /dev/null
+++ b/service/src/main/java/market/guess/service/repository/Repository.java
@@ -0,0 +1,12 @@
1package market.guess.service.repository;
2
3import java.util.List;
4import java.util.Optional;
5
6public interface Repository<T> {
7 T add(T element);
8
9 Optional<T> get(String id);
10
11 List<T> getAll();
12}
diff --git a/service/src/main/java/market/guess/service/repository/UserRepository.java b/service/src/main/java/market/guess/service/repository/UserRepository.java
index 78d620d..86cc9a6 100644
--- a/service/src/main/java/market/guess/service/repository/UserRepository.java
+++ b/service/src/main/java/market/guess/service/repository/UserRepository.java
@@ -1,11 +1,5 @@
1package market.guess.service.repository; 1package market.guess.service.repository;
2 2
3import java.math.BigDecimal;
4import market.guess.service.domain.User; 3import market.guess.service.domain.User;
5 4
6public interface UserRepository { 5public interface UserRepository extends Repository<User> {}
7
8 User getUser(String name);
9
10 void addUser(String name, BigDecimal initialBalance);
11}
diff --git a/test-data/ex-1/multiple.xml b/test-data/ex-1/multiple.xml
new file mode 100644
index 0000000..3f2432d
--- /dev/null
+++ b/test-data/ex-1/multiple.xml
@@ -0,0 +1,47 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<Guess-Market xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="GM-EX1-schema.xsd">
3 <GM-events>
4 <GM-event name="Mujtaba is Dead">
5 <id>1</id>
6 <description>This event gambles if Mujtaba is a live or not. it will be determined if he will be shown in public until 31.8.26</description>
7 <comision type="on-purchase">5</comision>
8 <GM-options>
9 <GM-option>Hell Yea !</GM-option>
10 <GM-option>No way !</GM-option>
11 </GM-options>
12 <GM-method>
13 <GM-LMSR>
14 <b>100</b>
15 </GM-LMSR>
16 </GM-method>
17 </GM-event>
18 <GM-event name="World Cap Winner">
19 <id>2</id>
20 <description>Who do you think will win the world cap ?</description>
21 <comision type="on-close">15</comision>
22 <GM-options>
23 <GM-option>Argentina</GM-option>
24 <GM-option>Spain</GM-option>
25 </GM-options>
26 <GM-method>
27 <GM-LMSR>
28 <b>50</b>
29 </GM-LMSR>
30 </GM-method>
31 </GM-event>
32 <GM-event name="Earth Quake on Dead Sea">
33 <id>3</id>
34 <description>Will there be an earth quake on the dead sea until the December 31th 2026</description>
35 <comision type="on-purchase">50</comision>
36 <GM-options>
37 <GM-option>Yes</GM-option>
38 <GM-option>No</GM-option>
39 </GM-options>
40 <GM-method>
41 <GM-LMSR>
42 <b>400</b>
43 </GM-LMSR>
44 </GM-method>
45 </GM-event>
46 </GM-events>
47</Guess-Market>
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;
4import market.guess.service.LocalGuessMarketContext; 4import market.guess.service.LocalGuessMarketContext;
5import market.guess.service.TradingOperation; 5import market.guess.service.TradingOperation;
6import market.guess.service.TradingOperationImpl; 6import market.guess.service.TradingOperationImpl;
7import market.guess.service.infrastructure.mapper.v1.EventMapperV1;
7import market.guess.service.infrastructure.provider.Loader; 8import market.guess.service.infrastructure.provider.Loader;
8import market.guess.service.infrastructure.provider.v1.XMLLoader; 9import market.guess.service.infrastructure.provider.v1.XMLLoaderV1;
9import market.guess.service.repository.EventRepository; 10import market.guess.service.repository.EventRepository;
10import market.guess.service.repository.InMemoryEventRepository; 11import market.guess.service.repository.InMemoryEventRepository;
11import market.guess.service.repository.InMemoryUserRepository; 12import market.guess.service.repository.InMemoryUserRepository;
12import market.guess.service.repository.UserRepository; 13import market.guess.service.repository.UserRepository;
14import market.guess.ui.console.command.ExitMenuCommand;
15import market.guess.ui.console.command.ListEventsMenuCommand;
13import market.guess.ui.console.command.LoadFileMenuCommand; 16import market.guess.ui.console.command.LoadFileMenuCommand;
14import market.guess.ui.console.io.ConsoleInputProvider; 17import market.guess.ui.console.io.ConsoleInputProvider;
15import market.guess.ui.console.io.ConsoleOutputProvider; 18import 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 @@
1package market.guess.ui.console.command;
2
3import market.guess.ui.console.io.OutputProvider;
4
5public 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 @@
1package market.guess.ui.console.command;
2
3import market.guess.api.CommissionTiming;
4import market.guess.api.GuessMarketContext;
5import market.guess.ui.console.io.OutputProvider;
6
7public 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}