refactor: 🔨 convert into library
This commit is contained in:
parent
2efa346c76
commit
356c88ae46
1
.github/img/todo.svg
vendored
Normal file
1
.github/img/todo.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 24"><path d="M44.65674,21.2998a3.0394,3.0394,0,0,1-2.4126-.875,3.41057,3.41057,0,0,1-.76269-2.32519,6.92661,6.92661,0,0,1,.03759-.71191q.03737-.36327.13769-.9375l.92481-5.5H39.28173l.47461-2.8501h2.0752A1.92861,1.92861,0,0,0,43.044,7.7749a1.76782,1.76782,0,0,0,.53759-1.1748l.5-3.0503H47.4815l-.75,4.5503h5.05029l-.4751,2.8501H46.25635l-1.19971,7.19921.5.17579,3.8999-3.0752,1.79981,2.0498-1.5,1.29981q-.79981.67529-1.44971,1.1875a10.42993,10.42993,0,0,1-1.23779.85059,5.22593,5.22593,0,0,1-1.16211.5A4.62212,4.62212,0,0,1,44.65674,21.2998Zm14.6748,0a5.46483,5.46483,0,0,1-4.10009-1.4873A5.56943,5.56943,0,0,1,53.78174,15.75a9.66527,9.66527,0,0,1,.53711-3.3252,7.20855,7.20855,0,0,1,1.5-2.5,6.50936,6.50936,0,0,1,2.30029-1.5747,7.71558,7.71558,0,0,1,2.9624-.5503,5.464,5.464,0,0,1,4.10009,1.4878,5.57305,5.57305,0,0,1,1.44971,4.0625,9.672,9.672,0,0,1-.53711,3.3247,7.21883,7.21883,0,0,1-1.5,2.5A6.50666,6.50666,0,0,1,62.294,20.75,7.72834,7.72834,0,0,1,59.33154,21.2998Zm.27491-2.7246a2.73119,2.73119,0,0,0,1.9624-.72559,3.57849,3.57849,0,0,0,.98779-2.0752l.32471-1.94921c.0332-.1504.0625-.32081.08789-.5127a4.01545,4.01545,0,0,0,.03711-.5127,2.26958,2.26958,0,0,0-.5874-1.7124,2.235,2.235,0,0,0-1.61231-.5625,2.7301,2.7301,0,0,0-1.9624.7251,3.57547,3.57547,0,0,0-.98779,2.0752l-.32471,1.94921c-.03369.15039-.0625.32129-.08789.5127a4.02821,4.02821,0,0,0-.03711.51269,2.2681,2.2681,0,0,0,.5874,1.7129A2.23342,2.23342,0,0,0,59.60645,18.5752Zm17.27441-.67579h-.125a12.32427,12.32427,0,0,1-.78711,1.373,5.54578,5.54578,0,0,1-.92529,1.07031,3.86152,3.86152,0,0,1-1.14991.70508,4.43681,4.43681,0,0,1-3.20019-.09765,2.96636,2.96636,0,0,1-1.16211-1.0127,4.63007,4.63007,0,0,1-.6626-1.58789,9.13873,9.13873,0,0,1-.2124-2.04981,13.27046,13.27046,0,0,1,.4248-3.4873,8.56935,8.56935,0,0,1,1.2002-2.6748,5.6303,5.6303,0,0,1,1.8623-1.7251,4.882,4.882,0,0,1,2.4375-.6128,3.32428,3.32428,0,0,1,2.3125.71875,3.66607,3.66607,0,0,1,1.0625,2.00538h.17481L79.45605,2.5H83.106L80.03125,21H76.38086Zm-2.82471.55079a2.2377,2.2377,0,0,0,1.19971-.32325,3.739,3.739,0,0,0,.9751-.89453,6.92872,6.92872,0,0,0,.67529-.98144,4.00289,4.00289,0,0,0,.5-1.42774l.34961-2.062a1.79174,1.79174,0,0,0-.3125-1.56494,2.18107,2.18107,0,0,0-1.6875-.54639,2.6884,2.6884,0,0,0-2.07471.78272,4.29282,4.29282,0,0,0-.9751,2.248l-.25,1.49024a3.83444,3.83444,0,0,0-.07518.50976q-.0249.28566-.02491.60743a3.19053,3.19053,0,0,0,.38769,1.541A1.38859,1.38859,0,0,0,74.05615,18.4502Zm15.2749,2.8496A5.46485,5.46485,0,0,1,85.231,19.8125,5.56945,5.56945,0,0,1,83.78125,15.75a9.66527,9.66527,0,0,1,.53711-3.3252,7.20855,7.20855,0,0,1,1.5-2.5,6.50945,6.50945,0,0,1,2.30029-1.5747,7.71565,7.71565,0,0,1,2.9624-.5503,5.464,5.464,0,0,1,4.1001,1.4878,5.57305,5.57305,0,0,1,1.44971,4.0625,9.672,9.672,0,0,1-.53711,3.3247,7.21883,7.21883,0,0,1-1.5,2.5A6.50662,6.50662,0,0,1,92.29346,20.75,7.72832,7.72832,0,0,1,89.33105,21.2998Zm.27491-2.7246a2.7312,2.7312,0,0,0,1.96241-.72559,3.57843,3.57843,0,0,0,.98779-2.0752l.32471-1.94921c.0332-.1504.0625-.32081.08789-.5127a4.01545,4.01545,0,0,0,.03711-.5127,2.26953,2.26953,0,0,0-.58741-1.7124,2.235,2.235,0,0,0-1.61231-.5625,2.73005,2.73005,0,0,0-1.9624.7251,3.57541,3.57541,0,0,0-.98779,2.0752l-.32471,1.94921c-.03369.15039-.0625.32129-.08789.5127a4.02638,4.02638,0,0,0-.03711.51269,2.26806,2.26806,0,0,0,.5874,1.7129,2.23345,2.23345,0,0,0,1.61235.5625Zm14.49952,2.7246a2.48881,2.48881,0,0,1-1.6875-.46289,1.44412,1.44412,0,0,1-.51221-1.1123,3.7521,3.7521,0,0,1,.0249-.3877q.02416-.2373.125-.8125a2.1494,2.1494,0,0,1,.75-1.33691,2.81154,2.81154,0,0,1,1.875-.53809,2.49057,2.49057,0,0,1,1.6875.46289,1.4487,1.4487,0,0,1,.51221,1.11231,3.86127,3.86127,0,0,1-.0249.38769c-.01661.15821-.0586.42969-.125.8125a2.15625,2.15625,0,0,1-.75,1.3379A2.81688,2.81688,0,0,1,104.10548,21.2998Zm.32518-6.3496-.30029-6.314.8252-5.08643h4.02491l-.8501,5.08643-2.3999,6.314Zm15.521-1.5312c.00553-4.31647,2.8785-8.54763,7.07519-10.919h3.5747c-3.306,1.97586-6.39479,4.74307-7.10013,8.17388l-.39987,1.9911c-.62683,3.139,1.23593,6.42127,3.7749,8.63482h-3.2749A10.3606,10.3606,0,0,1,119.95167,13.419ZM142,10.3804c-.0043,4.31742-2.878,8.54776-7.07373,10.9194h-3.57518c3.30582-1.97581,6.39557-4.74308,7.10062-8.17388l.40036-1.99109C139.47743,7.99569,137.615,4.714,135.07666,2.5h3.27544A10.36251,10.36251,0,0,1,142,10.3804ZM16,0,0,24H32Zm2,22H14V18h4Zm-4-6V8h4v8Z" style="fill:#ffb74d"/></svg>
|
||||||
|
After Width: | Height: | Size: 4.3 KiB |
661
Cargo.lock
generated
661
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
81
Cargo.toml
81
Cargo.toml
@ -1,48 +1,51 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustler"
|
name = "rustler-core"
|
||||||
version = "0.0.0-alpha.0"
|
version = "0.0.0-alpha.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "🤠 » single-action data extractor"
|
description = "🤠 » rustler-core market data extractor core functionality"
|
||||||
authors = ["Lucas Colombo <lucas@lucode.ar>"]
|
authors = ["Lucas Colombo <lucas@lucode.ar>"]
|
||||||
publish = false
|
build = "lib/build.rs"
|
||||||
|
|
||||||
[profile.release]
|
[lib]
|
||||||
strip = true
|
path = "lib/lib.rs"
|
||||||
lto = true
|
|
||||||
codegen-units = 16
|
|
||||||
opt-level = 'z'
|
|
||||||
panic = "abort"
|
|
||||||
rpath = false
|
|
||||||
overflow-checks = false
|
|
||||||
debug = 0
|
|
||||||
debug-assertions = false
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "rustler"
|
|
||||||
path = "app/main.rs"
|
|
||||||
|
|
||||||
[workspace]
|
|
||||||
members = [".", "migration", "entities", "grpc", "rustlers"]
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
lool = { version = "0.2.0", registry = "lugit" } # crates: disable-check
|
|
||||||
eyre = { version = "0.6.12", default-features = false }
|
|
||||||
tokio-tungstenite = { version = "0.21.0" }
|
|
||||||
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# internal
|
# utils
|
||||||
entities = { path = "entities" }
|
eyre = { version = "0.6.12", default-features = false }
|
||||||
grpc = { path = "grpc" }
|
|
||||||
rustlers = { path = "rustlers" }
|
|
||||||
|
|
||||||
# workspace
|
|
||||||
eyre = { workspace = true, default-features = false, features = [
|
|
||||||
"auto-install",
|
|
||||||
] }
|
|
||||||
lool = { workspace = true }
|
|
||||||
tokio = { workspace = true }
|
|
||||||
|
|
||||||
# external
|
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
chrono = "0.4.37"
|
chrono = "0.4.37"
|
||||||
|
getset = "0.1.2"
|
||||||
|
|
||||||
|
# async
|
||||||
|
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
|
||||||
|
async-trait = "0.1.79"
|
||||||
|
|
||||||
|
# grpc & websocket
|
||||||
|
tokio-tungstenite = { version = "0.21.0" }
|
||||||
|
tonic = "0.11.0"
|
||||||
|
prost = "0.12.3" # protocol buffers
|
||||||
|
|
||||||
|
# database
|
||||||
|
sea-orm = { version = "0.12.15", features = [
|
||||||
|
"runtime-tokio-native-tls",
|
||||||
|
"sqlx-sqlite",
|
||||||
|
"macros",
|
||||||
|
] }
|
||||||
|
sea-orm-migration = { version = "0.12.15", features = [
|
||||||
|
"runtime-tokio-native-tls",
|
||||||
|
"sqlx-sqlite",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[dependencies.lool]
|
||||||
|
version = "0.2.0" # crates: disable-check
|
||||||
|
registry = "lugit"
|
||||||
|
features = [
|
||||||
|
"cli.stylize",
|
||||||
|
"logger",
|
||||||
|
"sched.tokio",
|
||||||
|
"sched.rule-recurrence",
|
||||||
|
"macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tonic-build = "0.11.0"
|
||||||
|
|||||||
14
README.md
14
README.md
@ -22,13 +22,15 @@ Also, this library is built using the `Rust` programming language... so, ***rust
|
|||||||
|
|
||||||
This library defines the core functionality for a `rustler`. It includes the following:
|
This library defines the core functionality for a `rustler`. It includes the following:
|
||||||
|
|
||||||
- A `Rustler` trait that defines the core functionality for a `rustler`.
|
- A [`Rustler`](./lib/rustlers/rustlers.rs) trait that defines the core functionality for a `rustler`.
|
||||||
- A `RustlersSvc` which orchestrates the `rustlers` at runtime, scheduling them to scrape stock pricing data between market hours.
|
- A [`RustlersSvc`](./lib/rustlers/svc.rs) which orchestrates the `rustlers` at runtime, scheduling them to scrape stock pricing data between market hours.
|
||||||
|
|
||||||
Apart from the above, this library also defines:
|
Apart from the above, this library also defines:
|
||||||
|
|
||||||
- a database schema for storing market hours, which is used by the `RustlersSvc` to schedule the `rustlers`.
|
- a [database schema](./lib/entities/orm/) for storing market hours, which is used by the `RustlersSvc` to schedule the `rustlers`.
|
||||||
- a grpc service to interact with the rustlers database.
|
- initial [database migrations](./lib/entities/migration) to create the schema.
|
||||||
|
- a [grpc server](./lib/grpc/) to interact with the rustlers database.
|
||||||
|
- <img alt="unimplemented" src="./.github/img/todo.svg" height="12"> a [websocket gateway server](./lib/socket/) to stream stock pricing data to subscribed clients
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
>
|
>
|
||||||
@ -38,3 +40,7 @@ Apart from the above, this library also defines:
|
|||||||
> Although this library contains the core and abstract functionality for the rustlers, it doesn't include any concrete implementation for them.
|
> Although this library contains the core and abstract functionality for the rustlers, it doesn't include any concrete implementation for them.
|
||||||
>
|
>
|
||||||
> Actual concrete implementations for each market cannot be published for many reasons.
|
> Actual concrete implementations for each market cannot be published for many reasons.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Check the [examples](./examples) directory for an example of how to use this library.
|
||||||
|
|||||||
@ -16,7 +16,7 @@ tasks:
|
|||||||
run:watch:
|
run:watch:
|
||||||
desc: 🚀 watch rustler
|
desc: 🚀 watch rustler
|
||||||
cmds:
|
cmds:
|
||||||
- cargo watch -c -x "run"
|
- cargo watch -c -x "run --example=rustler"
|
||||||
|
|
||||||
build:watch:
|
build:watch:
|
||||||
desc: 🚀 watch rustler «build»
|
desc: 🚀 watch rustler «build»
|
||||||
|
|||||||
@ -1,60 +0,0 @@
|
|||||||
"""
|
|
||||||
small script to run after build, to check if there was a significant
|
|
||||||
change on executable size, compared to the previous build.
|
|
||||||
|
|
||||||
this aims to detect unwanted big differences before it's too late
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
import pathlib
|
|
||||||
|
|
||||||
curr_dir = pathlib.Path(os.getcwd())
|
|
||||||
sizefile_path = pathlib.Path(curr_dir.joinpath('.task'))
|
|
||||||
|
|
||||||
|
|
||||||
def bad(txt):
|
|
||||||
return '\033[91m' + txt + '\033[0m'
|
|
||||||
|
|
||||||
|
|
||||||
def good(txt):
|
|
||||||
return '\033[92m' + txt + '\033[0m'
|
|
||||||
|
|
||||||
|
|
||||||
def head(txt):
|
|
||||||
return '\033[94m' + txt + '\033[0m'
|
|
||||||
|
|
||||||
|
|
||||||
files = {
|
|
||||||
'release': curr_dir.joinpath('target/release/rustler.exe'),
|
|
||||||
'debug': curr_dir.joinpath('target/debug/rustler.exe'),
|
|
||||||
}
|
|
||||||
|
|
||||||
print("\n🧉 » exe file sizes change\n")
|
|
||||||
|
|
||||||
for key, exe in files.items():
|
|
||||||
if exe.is_file():
|
|
||||||
sizefile = sizefile_path.joinpath(key)
|
|
||||||
new_size: float = os.stat(exe).st_size / 1024
|
|
||||||
old_size: float
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(sizefile, 'r') as f:
|
|
||||||
old_size = float(f.read())
|
|
||||||
except FileNotFoundError:
|
|
||||||
old_size = 0
|
|
||||||
|
|
||||||
# diff: str = f'{old_size:.0f}kb'
|
|
||||||
diff = new_size - old_size
|
|
||||||
diff_str = f"{'+' if diff > 0 else '=' if diff == 0 else ''}{diff:.0f}kb"
|
|
||||||
|
|
||||||
fmt = bad if diff > 10 else good
|
|
||||||
|
|
||||||
sizefile.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
print(
|
|
||||||
f'{head(key)}: {fmt(diff_str)} (prev: {old_size}kb, now: {fmt(f"{str(new_size)}kb")})'
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(sizefile, 'w') as f:
|
|
||||||
f.write(f'{new_size}')
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "entities"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "entities"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# common
|
|
||||||
eyre = { workspace = true, default-features = false }
|
|
||||||
lool = { workspace = true, features = [ "cli", "cli.stylize", "macros", "logger" ] }
|
|
||||||
|
|
||||||
# external
|
|
||||||
sea-orm = { version = "0.12.15", features = [
|
|
||||||
"runtime-tokio-native-tls",
|
|
||||||
"sqlx-sqlite",
|
|
||||||
"macros",
|
|
||||||
] }
|
|
||||||
@ -1,33 +1,27 @@
|
|||||||
use {
|
use {
|
||||||
crate::{
|
async_trait::async_trait,
|
||||||
|
eyre::Result,
|
||||||
|
lool::logger::info,
|
||||||
|
rustler_core::{
|
||||||
rustler,
|
rustler,
|
||||||
rustlers::{Rustler, RustlerAccessor, RustlerStatus, Ticker},
|
rustlers::{Rustler, RustlerAccessor, RustlerStatus, Ticker},
|
||||||
},
|
},
|
||||||
async_trait::async_trait,
|
|
||||||
eyre::Result,
|
|
||||||
lool::{cli::stylize::Stylize, logger::info},
|
|
||||||
std::collections::HashMap,
|
std::collections::HashMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BINANCE_WSS_URL: &str = "wss://stream.binance.com:9443/stream";
|
|
||||||
// const MANUAL_CLOSE_CODE: u16 = 4663;
|
|
||||||
// const MANUAL_CLOSE_REASON: &str = "Manually Disconnected";
|
|
||||||
|
|
||||||
rustler!(
|
rustler!(
|
||||||
/// 🤠 » **binance rustler**
|
/// A fake rustler that does nothing but changing between different statuses.
|
||||||
///
|
pub struct FooRustler {}
|
||||||
/// A rustler that steals quotes from Binance
|
|
||||||
pub struct BinanceRustler {}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
impl BinanceRustler {
|
impl FooRustler {
|
||||||
pub fn create() -> impl Rustler {
|
pub fn create() -> impl Rustler {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Rustler for BinanceRustler {
|
impl Rustler for FooRustler {
|
||||||
async fn connect(&mut self) -> Result<()> {
|
async fn connect(&mut self) -> Result<()> {
|
||||||
if self.status == RustlerStatus::Connected || self.status == RustlerStatus::Connecting {
|
if self.status == RustlerStatus::Connected || self.status == RustlerStatus::Connecting {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -35,10 +29,7 @@ impl Rustler for BinanceRustler {
|
|||||||
|
|
||||||
self.set_status(RustlerStatus::Connecting)?;
|
self.set_status(RustlerStatus::Connecting)?;
|
||||||
|
|
||||||
info!(
|
info!("Connecting to data source");
|
||||||
"Connecting to Binance WSS: {}",
|
|
||||||
BINANCE_WSS_URL.bright_green()
|
|
||||||
);
|
|
||||||
|
|
||||||
self.set_status(RustlerStatus::Connected)?;
|
self.set_status(RustlerStatus::Connected)?;
|
||||||
|
|
||||||
@ -53,7 +44,7 @@ impl Rustler for BinanceRustler {
|
|||||||
|
|
||||||
self.set_status(RustlerStatus::Disconnecting)?;
|
self.set_status(RustlerStatus::Disconnecting)?;
|
||||||
|
|
||||||
info!("Disconnecting from Binance WSS");
|
info!("Disconnecting from data source");
|
||||||
|
|
||||||
self.set_status(RustlerStatus::Disconnected)?;
|
self.set_status(RustlerStatus::Disconnected)?;
|
||||||
|
|
||||||
@ -1,24 +1,25 @@
|
|||||||
|
mod binance;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
binance::FooRustler,
|
||||||
dotenvy::dotenv,
|
dotenvy::dotenv,
|
||||||
eyre::Result,
|
eyre::{set_hook, DefaultHandler, Result},
|
||||||
lool::logger::{info, ConsoleLogger, Level},
|
lool::logger::{info, ConsoleLogger, Level},
|
||||||
rustlers::{rustlerjar, rustlers::binance::BinanceRustler, svc::RustlersSvc},
|
rustler_core::{entities::db::get_connection, grpc, rustlerjar, rustlers::svc::RustlersSvc},
|
||||||
tokio::join,
|
tokio::join,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: here we will trigger the start of both the grpc server and the websocket gateway
|
|
||||||
// look at: https://github.com/hyperium/tonic/discussions/740
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
set_hook(Box::new(DefaultHandler::default_with))?;
|
||||||
ConsoleLogger::default_setup(Level::Trace, "rustler")?;
|
ConsoleLogger::default_setup(Level::Trace, "rustler")?;
|
||||||
|
|
||||||
dotenv()?;
|
dotenv()?;
|
||||||
let conn = entities::db::get_connection().await?;
|
let conn = get_connection().await?;
|
||||||
let mut rustler = RustlersSvc::new(
|
let mut rustler = RustlersSvc::new(
|
||||||
conn.clone(),
|
conn.clone(),
|
||||||
rustlerjar! {
|
rustlerjar! {
|
||||||
"BINANCE" => BinanceRustler
|
"BINANCE" => FooRustler
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -1,14 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "gateway"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "gateway"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
entities = { path = "../entities" }
|
|
||||||
eyre = { workspace = true, default-features = false }
|
|
||||||
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "grpc"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "grpc"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# common
|
|
||||||
lool = { workspace = true, features = ["logger"]}
|
|
||||||
eyre = { workspace = true, default-features = false }
|
|
||||||
|
|
||||||
# internal
|
|
||||||
entities = { path = "../entities" }
|
|
||||||
|
|
||||||
# external
|
|
||||||
prost = "0.12.3"
|
|
||||||
tonic = "0.11.0"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
tonic-build = "0.11.0"
|
|
||||||
|
|
||||||
@ -1,5 +1,9 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
let proto_files = vec!["./proto/rustler.proto", "./proto/market.proto", "./proto/ticker.proto"];
|
let proto_files = vec![
|
||||||
|
"./lib/grpc/proto/rustler.proto",
|
||||||
|
"./lib/grpc/proto/market.proto",
|
||||||
|
"./lib/grpc/proto/ticker.proto",
|
||||||
|
];
|
||||||
|
|
||||||
for proto_file in proto_files {
|
for proto_file in proto_files {
|
||||||
compile_proto(proto_file);
|
compile_proto(proto_file);
|
||||||
@ -1,6 +1,7 @@
|
|||||||
use sea_orm_migration::{async_trait::async_trait, prelude::*};
|
use sea_orm_migration::{async_trait::async_trait, prelude::*};
|
||||||
|
|
||||||
#[derive(DeriveMigrationName)]
|
#[derive(DeriveMigrationName)]
|
||||||
|
/// 🤠 » create table `market`
|
||||||
pub struct Migration;
|
pub struct Migration;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -1,6 +1,7 @@
|
|||||||
use sea_orm_migration::{async_trait::async_trait, prelude::*};
|
use sea_orm_migration::{async_trait::async_trait, prelude::*};
|
||||||
|
|
||||||
#[derive(DeriveMigrationName)]
|
#[derive(DeriveMigrationName)]
|
||||||
|
/// 🤠 » create table `ticker`
|
||||||
pub struct Migration;
|
pub struct Migration;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
2
lib/entities/migration/mod.rs
Normal file
2
lib/entities/migration/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mod m20220101_000001_create_table_market;
|
||||||
|
mod m20240325_200049_create_table_ticker;
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
pub mod migration;
|
||||||
pub use sea_orm;
|
pub use sea_orm;
|
||||||
|
|
||||||
mod orm {
|
mod orm {
|
||||||
@ -4,6 +4,7 @@ use sea_orm::entity::prelude::*;
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
#[sea_orm(table_name = "market")]
|
#[sea_orm(table_name = "market")]
|
||||||
|
/// 🤠 » market entity model
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@ -4,6 +4,7 @@ use sea_orm::entity::prelude::*;
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
#[sea_orm(table_name = "ticker")]
|
#[sea_orm(table_name = "ticker")]
|
||||||
|
/// 🤠 » ticker entity model
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@ -1,5 +1,5 @@
|
|||||||
use {
|
use {
|
||||||
crate::{
|
crate::entities::{
|
||||||
market::{Entity as Market, Model as MarketModel},
|
market::{Entity as Market, Model as MarketModel},
|
||||||
ticker::{Entity as Ticker, Model as TickerModel},
|
ticker::{Entity as Ticker, Model as TickerModel},
|
||||||
},
|
},
|
||||||
@ -7,6 +7,7 @@ use {
|
|||||||
sea_orm::{DatabaseConnection, DbErr, EntityTrait, IntoActiveModel},
|
sea_orm::{DatabaseConnection, DbErr, EntityTrait, IntoActiveModel},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// 🤠 » service for the `Market` entity
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
conn: DatabaseConnection,
|
conn: DatabaseConnection,
|
||||||
}
|
}
|
||||||
@ -16,16 +17,19 @@ impl Service {
|
|||||||
Self { conn }
|
Self { conn }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » gets all markets from the database
|
||||||
pub async fn get_all(&self) -> Result<Vec<MarketModel>, DbErr> {
|
pub async fn get_all(&self) -> Result<Vec<MarketModel>, DbErr> {
|
||||||
let markets = Market::find().all(&self.conn).await?;
|
let markets = Market::find().all(&self.conn).await?;
|
||||||
Ok(markets)
|
Ok(markets)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » gets a market by its id
|
||||||
pub async fn create(&self, market: MarketModel) -> Result<MarketModel, DbErr> {
|
pub async fn create(&self, market: MarketModel) -> Result<MarketModel, DbErr> {
|
||||||
Market::insert(market.clone().into_active_model()).exec(&self.conn).await?;
|
Market::insert(market.clone().into_active_model()).exec(&self.conn).await?;
|
||||||
Ok(market)
|
Ok(market)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » gets all markets with their tickers
|
||||||
pub async fn get_all_with_tickers(
|
pub async fn get_all_with_tickers(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<(MarketModel, Vec<TickerModel>)>, DbErr> {
|
) -> Result<Vec<(MarketModel, Vec<TickerModel>)>, DbErr> {
|
||||||
@ -1,5 +1,5 @@
|
|||||||
use {
|
use {
|
||||||
crate::{
|
crate::entities::{
|
||||||
orm::ticker,
|
orm::ticker,
|
||||||
ticker::{Entity as Ticker, Model as TickerModel},
|
ticker::{Entity as Ticker, Model as TickerModel},
|
||||||
},
|
},
|
||||||
@ -7,6 +7,7 @@ use {
|
|||||||
sea_orm::{ColumnTrait, DatabaseConnection, DbErr, EntityTrait, IntoActiveModel, QueryFilter},
|
sea_orm::{ColumnTrait, DatabaseConnection, DbErr, EntityTrait, IntoActiveModel, QueryFilter},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// 🤠 » service for the `Ticker` entity
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
conn: DatabaseConnection,
|
conn: DatabaseConnection,
|
||||||
}
|
}
|
||||||
@ -1,12 +1,15 @@
|
|||||||
mod services {
|
mod services {
|
||||||
use {
|
use {
|
||||||
entities::sea_orm::{DbErr, SqlErr},
|
|
||||||
lool::s,
|
lool::s,
|
||||||
|
sea_orm::{DbErr, SqlErr},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// market grpc services
|
||||||
pub mod market;
|
pub mod market;
|
||||||
|
/// ticker grpc services
|
||||||
pub mod ticker;
|
pub mod ticker;
|
||||||
|
|
||||||
|
/// general error handling for sql errors in grpc services
|
||||||
pub(crate) fn handle_sql_err(err: DbErr, action: &str, entity_name: &str) -> tonic::Status {
|
pub(crate) fn handle_sql_err(err: DbErr, action: &str, entity_name: &str) -> tonic::Status {
|
||||||
let sqlerr = err.sql_err();
|
let sqlerr = err.sql_err();
|
||||||
|
|
||||||
@ -1,15 +1,18 @@
|
|||||||
use {
|
use {
|
||||||
crate::services,
|
crate::{
|
||||||
entities::{market, sea_orm::DatabaseConnection, ticker},
|
entities::{market, ticker},
|
||||||
|
grpc::services,
|
||||||
|
},
|
||||||
eyre::Result,
|
eyre::Result,
|
||||||
lool::{cli::stylize::Stylize, logger::info},
|
lool::{cli::stylize::Stylize, logger::info},
|
||||||
|
sea_orm::DatabaseConnection,
|
||||||
std::net::SocketAddr,
|
std::net::SocketAddr,
|
||||||
tonic::transport::Server,
|
tonic::transport::Server,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RUSTLER_GRPC_API_ADDR: &str = "RUSTLER_GRPC_API_ADDR";
|
const RUSTLER_GRPC_API_ADDR: &str = "RUSTLER_GRPC_API_ADDR";
|
||||||
|
|
||||||
/// Starts the gRPC server
|
/// 🤠 » starts the rustler gRPC server
|
||||||
pub async fn start(conn: DatabaseConnection) -> Result<()> {
|
pub async fn start(conn: DatabaseConnection) -> Result<()> {
|
||||||
fn get_default_addr() -> String {
|
fn get_default_addr() -> String {
|
||||||
let addr = "0.0.0.0:50051";
|
let addr = "0.0.0.0:50051";
|
||||||
@ -36,8 +39,8 @@ pub async fn start(conn: DatabaseConnection) -> Result<()> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Server::builder()
|
Server::builder()
|
||||||
.add_service(market_grpc.svc())
|
.add_service(market_grpc.svc()) // add the market api
|
||||||
.add_service(ticker_grpc.svc())
|
.add_service(ticker_grpc.svc()) // add the ticker api
|
||||||
.serve(addr)
|
.serve(addr)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
use {
|
use {
|
||||||
self::market_mod::Empty,
|
self::market_mod::Empty,
|
||||||
crate::services::handle_sql_err,
|
crate::{entities::market, grpc::services::handle_sql_err},
|
||||||
entities::market,
|
|
||||||
eyre::Result,
|
eyre::Result,
|
||||||
lool::logger::{error, info},
|
lool::logger::{error, info},
|
||||||
market_mod::{
|
market_mod::{
|
||||||
@ -48,6 +47,7 @@ impl Market {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » grpc Server to manage market entities
|
||||||
pub struct GrpcServer {
|
pub struct GrpcServer {
|
||||||
pub(crate) svc: market::Service,
|
pub(crate) svc: market::Service,
|
||||||
}
|
}
|
||||||
@ -59,6 +59,7 @@ impl GrpcServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » creates the market api server
|
||||||
pub fn svc(self) -> MarketApiServer<GrpcServer> {
|
pub fn svc(self) -> MarketApiServer<GrpcServer> {
|
||||||
MarketApiServer::new(self)
|
MarketApiServer::new(self)
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
use {
|
use {
|
||||||
crate::services::handle_sql_err,
|
crate::{entities::ticker, grpc::services::handle_sql_err},
|
||||||
entities::ticker,
|
|
||||||
eyre::Result,
|
eyre::Result,
|
||||||
lool::logger::{error, info},
|
lool::logger::{error, info},
|
||||||
std::{any::Any, fmt::Debug, time::Instant},
|
std::{any::Any, fmt::Debug, time::Instant},
|
||||||
@ -33,6 +32,7 @@ impl Ticker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » grpc Server to manage ticker entities
|
||||||
pub struct GrpcServer {
|
pub struct GrpcServer {
|
||||||
pub(crate) svc: ticker::Service,
|
pub(crate) svc: ticker::Service,
|
||||||
}
|
}
|
||||||
@ -44,6 +44,7 @@ impl GrpcServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » creates the ticker api server
|
||||||
pub fn svc(self) -> TickerApiServer<GrpcServer> {
|
pub fn svc(self) -> TickerApiServer<GrpcServer> {
|
||||||
TickerApiServer::new(self)
|
TickerApiServer::new(self)
|
||||||
}
|
}
|
||||||
4
lib/lib.rs
Normal file
4
lib/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod entities;
|
||||||
|
pub mod grpc;
|
||||||
|
pub mod rustlers;
|
||||||
|
pub mod socket;
|
||||||
6
lib/rustlers/mod.rs
Normal file
6
lib/rustlers/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
mod rustler;
|
||||||
|
|
||||||
|
pub mod rustlerjar;
|
||||||
|
pub mod svc;
|
||||||
|
|
||||||
|
pub use rustler::*;
|
||||||
@ -1,11 +1,10 @@
|
|||||||
pub mod binance;
|
|
||||||
pub extern crate chrono;
|
pub extern crate chrono;
|
||||||
pub extern crate eyre;
|
pub extern crate eyre;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
crate::entities::{market, ticker},
|
||||||
async_trait::async_trait,
|
async_trait::async_trait,
|
||||||
chrono::{DateTime, Local},
|
chrono::{DateTime, Local},
|
||||||
entities::{market, ticker},
|
|
||||||
eyre::Result,
|
eyre::Result,
|
||||||
std::collections::HashMap,
|
std::collections::HashMap,
|
||||||
};
|
};
|
||||||
@ -59,6 +58,13 @@ pub struct ScrapperCallbacks {
|
|||||||
pub on_message: Option<fn(message: Quote) -> Result<()>>,
|
pub on_message: Option<fn(message: Quote) -> Result<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » a scruct representing a ticker
|
||||||
|
///
|
||||||
|
/// in `rustler` a ticker is the union between a symbol (stock identifier) and its market
|
||||||
|
///
|
||||||
|
/// they `key` of a ticker is the concatenation of the market and the symbol separated by a colon
|
||||||
|
///
|
||||||
|
/// e.g. `AAPL` in the `NASDAQ` market would have the key `NASDAQ:AAPL`
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Ticker {
|
pub struct Ticker {
|
||||||
pub symbol: String,
|
pub symbol: String,
|
||||||
@ -77,6 +83,7 @@ impl Ticker {
|
|||||||
tickers.iter().map(|t| Self::from(t, market)).collect()
|
tickers.iter().map(|t| Self::from(t, market)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 🤠 » returns the key of the ticker
|
||||||
pub fn key(&self) -> String {
|
pub fn key(&self) -> String {
|
||||||
format!("{}:{}", self.market, self.symbol)
|
format!("{}:{}", self.market, self.symbol)
|
||||||
}
|
}
|
||||||
@ -123,17 +130,17 @@ pub trait RustlerAccessor {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Rustler: RustlerAccessor + Send + Sync {
|
pub trait Rustler: RustlerAccessor + Send + Sync {
|
||||||
// #region Unimplemented trait functions
|
// #region Unimplemented trait functions
|
||||||
/// fn called after tickers are added to the rustler
|
/// 🤠 » fn called after tickers are added to the rustler
|
||||||
fn on_add(&mut self, tickers: &[Ticker]) -> Result<()>;
|
fn on_add(&mut self, tickers: &[Ticker]) -> Result<()>;
|
||||||
/// fn called after tickers are deleted from the rustler
|
/// 🤠 » fn called after tickers are deleted from the rustler
|
||||||
fn on_delete(&mut self, tickers: &[Ticker]) -> Result<()>;
|
fn on_delete(&mut self, tickers: &[Ticker]) -> Result<()>;
|
||||||
/// connects the rustler to the data source
|
/// 🤠 » connects the rustler to the data source
|
||||||
async fn connect(&mut self) -> Result<()>;
|
async fn connect(&mut self) -> Result<()>;
|
||||||
/// disconnects the rustler from the data source
|
/// 🤠 » disconnects the rustler from the data source
|
||||||
async fn disconnect(&mut self) -> Result<()>;
|
async fn disconnect(&mut self) -> Result<()>;
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
/// should be called at construction time
|
/// 🤠 » starts the rustler
|
||||||
async fn start(&mut self) -> Result<()> {
|
async fn start(&mut self) -> Result<()> {
|
||||||
let opts = self.opts();
|
let opts = self.opts();
|
||||||
if opts.connect_on_start {
|
if opts.connect_on_start {
|
||||||
@ -142,7 +149,7 @@ pub trait Rustler: RustlerAccessor + Send + Sync {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// updates last stop and last run times and calls the appropriate callback
|
/// 🤠 » updates last stop and last run times and calls the appropriate callback
|
||||||
///
|
///
|
||||||
/// should be called after the status of the rustler changes
|
/// should be called after the status of the rustler changes
|
||||||
fn handle_status_change(&mut self) -> Result<()> {
|
fn handle_status_change(&mut self) -> Result<()> {
|
||||||
@ -333,7 +340,11 @@ macro_rules! rustler_accessors {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the macro
|
/// **🤠 » rustler builder macro**
|
||||||
|
///
|
||||||
|
/// The `rustler!` macro is used to define a new `Rustler` struct, expanding the struct definition
|
||||||
|
/// with the required fields and derives, and implementing the `RustlerAccessor` trait for the
|
||||||
|
/// struct.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! rustler {
|
macro_rules! rustler {
|
||||||
// Entry point for the macro, takes the struct definition
|
// Entry point for the macro, takes the struct definition
|
||||||
@ -1,6 +1,6 @@
|
|||||||
use {
|
use {
|
||||||
crate::rustlers::Rustler,
|
super::rustler::Rustler,
|
||||||
entities::market,
|
crate::entities::market,
|
||||||
std::{collections::HashMap, sync::Arc},
|
std::{collections::HashMap, sync::Arc},
|
||||||
tokio::sync::Mutex,
|
tokio::sync::Mutex,
|
||||||
};
|
};
|
||||||
@ -34,7 +34,7 @@ macro_rules! rustlerjar {
|
|||||||
instances.push(instance);
|
instances.push(instance);
|
||||||
)*
|
)*
|
||||||
|
|
||||||
$crate::rustlerjar::RustlerJar::new(instances, mappings)
|
$crate::rustlers::rustlerjar::RustlerJar::new(instances, mappings)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,5 +1,9 @@
|
|||||||
use {
|
use {
|
||||||
entities::{market, sea_orm::DatabaseConnection, ticker},
|
super::{
|
||||||
|
rustler::{Rustler, Ticker},
|
||||||
|
rustlerjar::RustlerJar,
|
||||||
|
},
|
||||||
|
crate::entities::{market, sea_orm::DatabaseConnection, ticker},
|
||||||
eyre::Result,
|
eyre::Result,
|
||||||
lool::{
|
lool::{
|
||||||
logger::{info, warn},
|
logger::{info, warn},
|
||||||
@ -12,11 +16,6 @@ use {
|
|||||||
tokio::sync::Mutex,
|
tokio::sync::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
rustlerjar::RustlerJar,
|
|
||||||
rustlers::{Rustler, Ticker},
|
|
||||||
};
|
|
||||||
|
|
||||||
// interface MarketExecData {
|
// interface MarketExecData {
|
||||||
// entity: MarketModel;
|
// entity: MarketModel;
|
||||||
// startJob?: Job;
|
// startJob?: Job;
|
||||||
@ -34,6 +33,9 @@ use crate::{
|
|||||||
// stopJob?: Job,
|
// stopJob?: Job,
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/// **🤠 » Rustlers Service**
|
||||||
|
///
|
||||||
|
/// The `RustlersSvc` is a service that manages the rustlers and orchestrates their executions.
|
||||||
pub struct RustlersSvc {
|
pub struct RustlersSvc {
|
||||||
market_svc: market::Service,
|
market_svc: market::Service,
|
||||||
sched: Scheduler,
|
sched: Scheduler,
|
||||||
@ -41,7 +43,16 @@ pub struct RustlersSvc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RustlersSvc {
|
impl RustlersSvc {
|
||||||
|
/// **🤠 » create service**
|
||||||
|
///
|
||||||
/// creates a new instance of the `RustlersSvc`
|
/// creates a new instance of the `RustlersSvc`
|
||||||
|
///
|
||||||
|
/// **Arguments**
|
||||||
|
/// - `conn` - the database connection that will be used to get market and tickers data
|
||||||
|
/// - `rustlers` - the rustlers to be used by the service
|
||||||
|
///
|
||||||
|
/// **Returns**
|
||||||
|
/// the created `RustlersSvc` instance
|
||||||
pub async fn new(conn: DatabaseConnection, rustlers: RustlerJar) -> Self {
|
pub async fn new(conn: DatabaseConnection, rustlers: RustlerJar) -> Self {
|
||||||
let market_svc = market::Service::new(conn).await;
|
let market_svc = market::Service::new(conn).await;
|
||||||
let sched = Scheduler::new();
|
let sched = Scheduler::new();
|
||||||
@ -53,6 +64,8 @@ impl RustlersSvc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// **🤠 » start rustlers**
|
||||||
|
///
|
||||||
/// gets market data from the the database and starts
|
/// gets market data from the the database and starts
|
||||||
/// the corresponding rustler for each market
|
/// the corresponding rustler for each market
|
||||||
pub async fn start(&mut self) -> Result<()> {
|
pub async fn start(&mut self) -> Result<()> {
|
||||||
@ -66,6 +79,8 @@ impl RustlersSvc {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// **🤠 » restart rustlers**
|
||||||
|
///
|
||||||
/// stops all rustlers and then starts them again
|
/// stops all rustlers and then starts them again
|
||||||
pub async fn restart(&self) -> Result<()> {
|
pub async fn restart(&self) -> Result<()> {
|
||||||
// TODO: restart all rustlers; this method should clear everything we set up about the
|
// TODO: restart all rustlers; this method should clear everything we set up about the
|
||||||
@ -222,6 +237,9 @@ impl RustlersSvc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// creates a rule for the given time and offset
|
||||||
|
///
|
||||||
|
/// TODO: handle timezones
|
||||||
fn make_rule(
|
fn make_rule(
|
||||||
base: &RecurrenceRuleSet,
|
base: &RecurrenceRuleSet,
|
||||||
time: (u32, u32, u32),
|
time: (u32, u32, u32),
|
||||||
@ -239,6 +257,7 @@ fn make_rule(
|
|||||||
rule
|
rule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the operation to be performed on the time (addition or subtraction)
|
||||||
enum Op {
|
enum Op {
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
1
lib/socket/mod.rs
Normal file
1
lib/socket/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
// TODO: websocket gateway
|
||||||
@ -1,20 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "migration"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "migration"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
async-std = { version = "1", features = ["attributes", "tokio1"] }
|
|
||||||
entities = { path = "../entities" }
|
|
||||||
|
|
||||||
[dependencies.sea-orm-migration]
|
|
||||||
version = "0.12.15"
|
|
||||||
features = [
|
|
||||||
"runtime-tokio-native-tls",
|
|
||||||
"sqlx-sqlite",
|
|
||||||
]
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
# Running Migrator CLI
|
|
||||||
|
|
||||||
- Generate a new migration file
|
|
||||||
```sh
|
|
||||||
cargo run -- generate MIGRATION_NAME
|
|
||||||
```
|
|
||||||
- Apply all pending migrations
|
|
||||||
```sh
|
|
||||||
cargo run
|
|
||||||
```
|
|
||||||
```sh
|
|
||||||
cargo run -- up
|
|
||||||
```
|
|
||||||
- Apply first 10 pending migrations
|
|
||||||
```sh
|
|
||||||
cargo run -- up -n 10
|
|
||||||
```
|
|
||||||
- Rollback last applied migrations
|
|
||||||
```sh
|
|
||||||
cargo run -- down
|
|
||||||
```
|
|
||||||
- Rollback last 10 applied migrations
|
|
||||||
```sh
|
|
||||||
cargo run -- down -n 10
|
|
||||||
```
|
|
||||||
- Drop all tables from the database, then reapply all migrations
|
|
||||||
```sh
|
|
||||||
cargo run -- fresh
|
|
||||||
```
|
|
||||||
- Rollback all applied migrations, then reapply all migrations
|
|
||||||
```sh
|
|
||||||
cargo run -- refresh
|
|
||||||
```
|
|
||||||
- Rollback all applied migrations
|
|
||||||
```sh
|
|
||||||
cargo run -- reset
|
|
||||||
```
|
|
||||||
- Check the status of all migrations
|
|
||||||
```sh
|
|
||||||
cargo run -- status
|
|
||||||
```
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
use sea_orm_migration::async_trait::async_trait;
|
|
||||||
pub use sea_orm_migration::prelude::*;
|
|
||||||
|
|
||||||
mod m20220101_000001_create_table_market;
|
|
||||||
mod m20240325_200049_create_table_ticker;
|
|
||||||
|
|
||||||
pub struct Migrator;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl MigratorTrait for Migrator {
|
|
||||||
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
|
||||||
vec![
|
|
||||||
Box::new(m20220101_000001_create_table_market::Migration),
|
|
||||||
Box::new(m20240325_200049_create_table_ticker::Migration),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn migration_table_name() -> sea_orm::DynIden {
|
|
||||||
Alias::new("migrations").into_iden()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
use sea_orm_migration::prelude::*;
|
|
||||||
|
|
||||||
#[async_std::main]
|
|
||||||
async fn main() {
|
|
||||||
cli::run_cli(migration::Migrator).await;
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "rustlers"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "rustlers"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# workspace
|
|
||||||
eyre = { workspace = true, default-features = false }
|
|
||||||
tokio-tungstenite = { workspace = true }
|
|
||||||
tokio = { workspace = true }
|
|
||||||
lool = { workspace = true, features = [
|
|
||||||
"logger",
|
|
||||||
"sched.tokio",
|
|
||||||
"sched.rule-recurrence",
|
|
||||||
"macros",
|
|
||||||
] }
|
|
||||||
|
|
||||||
# internal
|
|
||||||
entities = { path = "../entities" }
|
|
||||||
|
|
||||||
# external
|
|
||||||
chrono = "0.4.37"
|
|
||||||
async-trait = "0.1.79"
|
|
||||||
getset = "0.1.2"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
tonic-build = "0.11.0"
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
pub mod rustlerjar;
|
|
||||||
pub mod rustlers;
|
|
||||||
pub mod svc;
|
|
||||||
Loading…
x
Reference in New Issue
Block a user