Add base for parsing config, wallets & trades

This commit is contained in:
Glenn Y. Rolland 2021-04-04 14:47:31 +02:00
parent 7328239ac1
commit 207b417c3d
19 changed files with 378 additions and 24 deletions

54
src/actions.cr Normal file
View file

@ -0,0 +1,54 @@
module Hodler
class Action
enum Type
Report
TextUi
WebUi
end
def self.match(action)
return false
end
def initialize(config : ConfigModel)
@config = config
end
def perform
wallets = {} of Tuple(String,String) => Float32
@config.trades.each do |trade|
tr = trade.transaction
from_id = {tr.from.wallet, tr.from.sym}
to_id = {tr.to.wallet, tr.to.sym}
fee_id = {tr.from.wallet, tr.fee.sym}
wallets[from_id] ||= 0
wallets[fee_id] ||= 0
wallets[to_id] ||= 0
wallets[from_id] -= tr.from.amount
wallets[fee_id] -= tr.fee.amount
wallets[to_id] += tr.from.amount
pp tr
pp wallets
end
end
end
class ActionFactory
def self.build(options, config)
action_class = Action
[ReportAction, WebUiAction, TextUiAction].each do |cls|
action_class = cls if cls.match(options[:action])
end
action = action_class.new(config)
end
end
end
require "./actions/report"
require "./actions/tui"
require "./actions/web"

10
src/actions/report.cr Normal file
View file

@ -0,0 +1,10 @@
require "../actions"
module Hodler
class ReportAction < Action
def self.match(action)
return (action == Action::Type::Report)
end
end
end

10
src/actions/tui.cr Normal file
View file

@ -0,0 +1,10 @@
require "../actions"
module Hodler
class TextUiAction < Action
def self.match(action)
return (action == Action::Type::TextUi)
end
end
end

10
src/actions/web.cr Normal file
View file

@ -0,0 +1,10 @@
require "../actions"
module Hodler
class WebUiAction < Action
def self.match(action)
return (action == Action::Type::WebUi)
end
end
end

View file

@ -1,58 +1,144 @@
require "./models"
require "./actions"
require "./models"
module Hodler
class Cli
alias Options = {
wallet_file: String,
action: Action::Type,
verbose_enable: Bool,
config_file: String
}
# @config : Config?
@options : Options?
property config : ConfigModel?
property options : Options?
def initialize
# @config = nil
@config = nil
@options = nil
end
def parse_options(args) : Options
def self.parse_options(args) : Options
# default values
wallet_file = XDGBasedir.full_path("hodler/wallet.yml", :config, :read).as(String)
action = Action::Type::Report
config_file = XDGBasedir.full_path("hodler/wallet.yml", :config, :read).as(String)
verbose_enable = true
# parse
OptionParser.parse(args) do |parser|
parser.banner = "Usage: hodler [arguments]"
parser.banner = "Usage: #{Version::PROGRAM} [options] [commands] [arguments]"
parser.on "-w WALLET_FILE", "--wallet=WALLET_FILE", "Use the following wallet file" do |file|
wallet_file = file
parser.separator
parser.separator "Options"
parser.on "-c CONFIG_FILE", "--config=CONFIG_FILE", "Use the following config file" do |file|
config_file = file
end
# parser.on "-f DOCKER_COMPOSE_YML", "--config=DOCKER_COMPOSE_YML", "Use the following docker-compose file" do |file|
# docker_compose_yml = file
# end
parser.on "-v", "--verbose", "Be verbose" do
verbose_enable = !verbose_enable
end
parser.on "-v", "--version", "Show version" do
parser.on "--version", "Show version" do
puts "version #{Version::VERSION}"
exit 0
end
parser.on "-h", "--help", "Show help" do
parser.on "-h", "--help", "Show this help" do
puts parser
exit 0
end
complete_with "hodler", parser
parser.on "--completion", "Provide autocompletion for bash" do
# nothing here
end
complete_with Version::PROGRAM, parser
parser.separator
parser.separator "Commands"
parser.on("report", "Compute report") do
parser.banner = "Usage: #{Version::PROGRAM} list [arguments]"
action = Action::Type::Report
end
parser.on("tui", "Run ncurses interactive UI") do
parser.banner = "Usage: #{Version::PROGRAM} tui [arguments]"
action = Action::Type::TextUi
end
parser.on("web", "Run web interactive UI") do
parser.banner = "Usage: #{Version::PROGRAM} web [arguments]"
action = Action::Type::WebUi
end
parser.separator
parser.missing_option do |flag|
STDERR.puts parser
STDERR.puts "ERROR: #{flag} requires an argument.".colorize(:red)
exit(1)
end
parser.invalid_option do |flag|
STDERR.puts parser
STDERR.puts "ERROR: #{flag} is not a valid option.".colorize(:red)
exit(1)
end
parser.unknown_args do |unknown_args|
unknown_args = unknown_args - %w(report web tui)
next if unknown_args.empty?
STDERR.puts parser
STDERR.puts "ERROR: \"#{unknown_args[0]}\" is not a valid command.".colorize(:red)
exit(1)
end
end
@options = {
wallet_file: wallet_file
}
return {
verbose_enable: verbose_enable,
config_file: config_file,
action: action
}.as(Options)
end
return @options.as(Options)
def self.parse_config(options)
puts "Loading configuration...".colorize(:yellow) if options[:verbose_enable]
config_file = options[:config_file]
if ! File.exists? config_file
STDERR.puts "ERROR: Unable to read configuration file '#{config_file}'".colorize(:red)
exit 1
end
yaml_str = File.read(config_file)
config = ConfigModel.from_yaml(yaml_str)
if config.nil?
STDERR.puts "ERROR: Invalid YAML content in '#{config_file}'"
exit 1
end
return config
end
def self.run(args)
app = Cli.new
app.parse_options(args)
# config = app.load_config(opts["config_file"])
# env_config = App.get_config(config, opts["environment"])
options = Cli.parse_options(args)
config = Cli.parse_config(options)
app.options = options
app.config = config
portfolio = PortfolioFactory.build(options, config)
action = ActionFactory.build(options, config)
action.perform
rescue ex : YAML::ParseException
STDERR.puts "ERROR: #{ex.message}".colorize(:red)
end
end
end

View file

@ -5,10 +5,10 @@ require "colorize"
require "xdg_basedir"
require "completion"
# require "./config"
require "./version"
require "./types"
require "./models"
require "./cli"
Hodler::Cli.run(ARGV)

21
src/models.cr Normal file
View file

@ -0,0 +1,21 @@
require "yaml"
module Hodler
class Model
include YAML::Serializable
end
end
require "./types"
require "./models/config"
require "./models/deposit"
require "./models/fee_trade"
require "./models/portfolio"
require "./models/ref_wallet"
require "./models/trade"
require "./models/transaction_trade"
require "./models/ui_config"
require "./models/wallet"
require "./models/wallet_value_trade"

13
src/models/config.cr Normal file
View file

@ -0,0 +1,13 @@
require "../models"
module Hodler
class ConfigModel < Model
property version : String
property ui : UiConfigModel
property wallets : Array(WalletModel)
property deposits : Array(DepositModel)
property trades : Array(TradeModel)
end
end

12
src/models/deposit.cr Normal file
View file

@ -0,0 +1,12 @@
require "../models"
module Hodler
class DepositModel < Model
include YAML::Serializable
property id : String?
property date : String
property deposit : WalletValueTradeModel
end
end

9
src/models/fee_trade.cr Normal file
View file

@ -0,0 +1,9 @@
require "../models"
module Hodler
class FeeTradeModel < Model
property amount : AmountT
property sym : String
end
end

36
src/models/portfolio.cr Normal file
View file

@ -0,0 +1,36 @@
require "../models"
module Hodler
class PortfolioModel < Model
property wallets : Array(WalletModel)
def initialize
@wallets = [] of WalletModel
end
def import_wallets(wallets : Array(WalletModel))
wallets.each do |wallet_config|
wallet = WalletModel.new(wallet_config.name, wallet_config.type)
# wallet.import(wallet_config)
pp wallet_config
end
end
def add_wallet
end
def remove_wallet
end
end
class PortfolioFactory
def self.build(options : Cli::Options, config : ConfigModel)
portfolio = PortfolioModel.new
portfolio.import_wallets(config.wallets)
end
end
end

10
src/models/ref_wallet.cr Normal file
View file

@ -0,0 +1,10 @@
require "../models"
module Hodler
class RefWalletModel < Model
property sym : String
property address : String?
end
end

12
src/models/trade.cr Normal file
View file

@ -0,0 +1,12 @@
require "../models"
module Hodler
class TradeModel < Model
include YAML::Serializable
property id : String?
property date : String
property transaction : TransactionTradeModel
end
end

View file

@ -0,0 +1,11 @@
require "yaml"
module Hodler
class TransactionTradeModel < Model
property from : WalletValueTradeModel
property to : WalletValueTradeModel
property fee : FeeTradeModel
end
end

11
src/models/ui_config.cr Normal file
View file

@ -0,0 +1,11 @@
require "../models"
module Hodler
class UiConfigModel < Model
property refresh_per_sec : UInt32
property default_sym : String
property columns : Array(String)
end
end

34
src/models/wallet.cr Normal file
View file

@ -0,0 +1,34 @@
require "../models"
module Hodler
class WalletModel < Model
enum Type
Web
Software
Cold
end
property name : String
property type : Type
property refs : Array(RefWalletModel)
def initialize(name, type : Type)
@name = name
@type = type
@refs = [] of RefWalletModel
end
def add_symbol
end
def remove_symbol
end
def add_transaction
end
def remove_transaction
end
end
end

View file

@ -0,0 +1,10 @@
require "../models"
module Hodler
class WalletValueTradeModel < Model
property wallet : String
property amount : AmountT
property sym : String
end
end

4
src/types.cr Normal file
View file

@ -0,0 +1,4 @@
module Hodler
alias AmountT = Float32 | Int32
end

View file

@ -1,6 +1,7 @@
module Hodler
class Version
PROGRAM = "hodler"
VERSION = "0.1.0"
end
end