0.1.0
This commit is contained in:
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
Cargo.lock
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# mails
|
||||||
|
*.eml
|
||||||
|
# Node
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
.settings
|
||||||
|
.project
|
||||||
|
|
||||||
|
# PID
|
||||||
|
*.pid
|
||||||
|
|
||||||
|
# var folder
|
||||||
|
var/
|
||||||
|
# env
|
||||||
|
.env
|
||||||
|
!.env.dev
|
||||||
|
config.json
|
||||||
|
|
||||||
|
# sqlite
|
||||||
|
*.db
|
||||||
|
*.db-shm
|
||||||
|
*.db-wal
|
||||||
47
Cargo.toml
Normal file
47
Cargo.toml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# file: Cargo.toml
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
resolver = "3"
|
||||||
|
members = [
|
||||||
|
"khbb_lib",
|
||||||
|
"khbb_listener_app",
|
||||||
|
]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobot"
|
||||||
|
authors = ["SinuS von SifriduS <sinus@sasedev.net>"]
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
async-trait = { version = "^0.1", features = [] }
|
||||||
|
base64 = { version = "^0.22", features = [] }
|
||||||
|
chrono = { version = "^0.4", features = ["serde"] }
|
||||||
|
futures-util = { version = "^0.3", features = [] }
|
||||||
|
reqwest = { version = "^0.13", default-features = false, features = ["charset", "cookies", "deflate", "form", "gzip", "http2", "json", "multipart", "query", "rustls", "socks", "stream", "zstd"] }
|
||||||
|
rustls = { version = "^0.23", features = ["aws-lc-rs"] }
|
||||||
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
|
serde_json = { version = "^1.0", features = [] }
|
||||||
|
solana-account-decoder-client-types = { version = "4.0.0-beta.7", features = ["zstd"] }
|
||||||
|
solana-address-lookup-table-interface = { version = "^3.0", features = ["bincode", "serde"] }
|
||||||
|
solana-client = { version = "^3.1", features = [] }
|
||||||
|
solana-compute-budget-interface = { version = "^3.0", features = ["borsh", "serde"] }
|
||||||
|
solana-rpc-client-api = { version = "4.0.0-beta.7", features = [] }
|
||||||
|
solana-sdk = { version = "^4.0", features = ["full"] }
|
||||||
|
solana-sdk-ids = { version = "^3.1", features = [] }
|
||||||
|
solana-system-interface = { version = "^3.0", features = ["alloc", "bincode", "serde", "std"] }
|
||||||
|
solana-transaction-status-client-types = { version = "4.0.0-beta.7", features = [] }
|
||||||
|
spl-associated-token-account-interface = { version = "^2.0", features = ["borsh"] }
|
||||||
|
spl-memo-interface = { version = "^2.0", features = [] }
|
||||||
|
spl-token-interface = { version = "^2.0", features = [] }
|
||||||
|
spl-token-2022-interface = { version = "^2.1", features = [] }
|
||||||
|
sqlx = { version = "^0.8", features = ["chrono", "uuid", "bigdecimal", "json", "sqlite", "runtime-tokio-rustls"] }
|
||||||
|
tokio = { version = "^1.52", features = ["full"] }
|
||||||
|
tokio-stream = { version = "^0.1", features = ["full"] }
|
||||||
|
tokio-tungstenite = { version = "^0.29", default-features = false, features = ["connect", "handshake", "rustls-tls-webpki-roots", "stream", "url"] }
|
||||||
|
tracing = { version = "^0.1", features = [] }
|
||||||
|
tracing-subscriber = { version = "^0.3", features = ["ansi", "env-filter", "chrono", "serde", "json"] }
|
||||||
|
yellowstone-grpc-client = { version = "^13.0", features = [] }
|
||||||
|
yellowstone-grpc-proto = { version = "^12.2", features = [] }
|
||||||
34
clippy.toml
Normal file
34
clippy.toml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# file: clippy.toml
|
||||||
|
|
||||||
|
msrv = "1.85.0"
|
||||||
|
|
||||||
|
# The project favors explicit control flow and visible intent.
|
||||||
|
# These settings complement the coding rules already enforced manually
|
||||||
|
# in code review: no `?`, no `unwrap`, no `expect`, explicit error paths.
|
||||||
|
|
||||||
|
too-many-arguments-threshold = 8
|
||||||
|
type-complexity-threshold = 250
|
||||||
|
single-char-binding-names-threshold = 3
|
||||||
|
trivial-copy-size-limit = 16
|
||||||
|
pass-by-value-size-limit = 256
|
||||||
|
stack-size-threshold = 512000
|
||||||
|
vec-box-size-threshold = 4096
|
||||||
|
max-fn-params-bools = 2
|
||||||
|
max-include-file-size = 1048576
|
||||||
|
cognitive-complexity-threshold = 25
|
||||||
|
too-large-for-stack = 2048
|
||||||
|
enum-variant-size-threshold = 200
|
||||||
|
large-error-threshold = 128
|
||||||
|
avoid-breaking-exported-api = true
|
||||||
|
disallowed-macros = []
|
||||||
|
disallowed-methods = []
|
||||||
|
disallowed-names = ["foo", "bar", "baz", "tmp"]
|
||||||
|
disallowed-types = []
|
||||||
|
allowed-idents-below-min-chars = [
|
||||||
|
"id",
|
||||||
|
"tx",
|
||||||
|
"rx",
|
||||||
|
"ms",
|
||||||
|
"pcm",
|
||||||
|
"vad",
|
||||||
|
]
|
||||||
40
khbb_lib/Cargo.toml
Normal file
40
khbb_lib/Cargo.toml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# file: khbb_lib/Cargo.toml
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "khbb_lib"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-trait.workspace = true
|
||||||
|
base64.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
|
futures-util.workspace = true
|
||||||
|
reqwest.workspace = true
|
||||||
|
rustls.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
solana-account-decoder-client-types.workspace = true
|
||||||
|
solana-address-lookup-table-interface.workspace = true
|
||||||
|
solana-client.workspace = true
|
||||||
|
solana-compute-budget-interface.workspace = true
|
||||||
|
solana-rpc-client-api.workspace = true
|
||||||
|
solana-sdk.workspace = true
|
||||||
|
solana-sdk-ids.workspace = true
|
||||||
|
solana-system-interface.workspace = true
|
||||||
|
solana-transaction-status-client-types.workspace = true
|
||||||
|
spl-associated-token-account-interface.workspace = true
|
||||||
|
spl-memo-interface.workspace = true
|
||||||
|
spl-token-2022-interface.workspace = true
|
||||||
|
spl-token-interface.workspace = true
|
||||||
|
sqlx.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
tokio-stream.workspace = true
|
||||||
|
tokio-tungstenite.workspace = true
|
||||||
|
tracing.workspace = true
|
||||||
|
tracing-subscriber.workspace = true
|
||||||
|
yellowstone-grpc-client.workspace = true
|
||||||
|
yellowstone-grpc-proto.workspace = true
|
||||||
33
khbb_lib/README.md
Normal file
33
khbb_lib/README.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<!-- file: khbb_lib/README.md -->
|
||||||
|
|
||||||
|
# khbb_lib
|
||||||
|
|
||||||
|
Core library for the `khadhroony-bobot` workspace.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- centralize reusable logic
|
||||||
|
- expose explicit APIs to binaries
|
||||||
|
- provide Solana RPC HTTP / WS / gRPC integrations
|
||||||
|
- provide storage and domain layers
|
||||||
|
- avoid hidden logic in binaries
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- no `anyhow`
|
||||||
|
- no `thiserror`
|
||||||
|
- no `?`
|
||||||
|
- no `unwrap` / `expect`
|
||||||
|
- explicit error handling
|
||||||
|
- async first
|
||||||
|
- `tracing`
|
||||||
|
- no `mod.rs`
|
||||||
|
- no `pub mod`
|
||||||
|
- `pub use` only from `lib.rs`
|
||||||
|
|
||||||
|
## Initial scope
|
||||||
|
|
||||||
|
- config loading
|
||||||
|
- tracing initialization
|
||||||
|
- SQLite connectivity
|
||||||
|
- listener runtime bootstrap
|
||||||
22
khbb_lib/TODO.md
Normal file
22
khbb_lib/TODO.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!-- file: khbb_lib/TODO.md -->
|
||||||
|
|
||||||
|
# khbb_lib TODO
|
||||||
|
|
||||||
|
## Foundation
|
||||||
|
|
||||||
|
- [x] create public library entrypoint
|
||||||
|
- [x] create explicit error type
|
||||||
|
- [x] create config loader
|
||||||
|
- [x] create tracing bootstrap
|
||||||
|
- [x] create listener app runner
|
||||||
|
|
||||||
|
## Next
|
||||||
|
|
||||||
|
- [ ] add storage layer
|
||||||
|
- [ ] add SQLite schema bootstrap
|
||||||
|
- [ ] add HTTP RPC client layer based on `reqwest`
|
||||||
|
- [ ] add WebSocket RPC client layer based on `tokio-tungstenite`
|
||||||
|
- [ ] add Yellowstone gRPC client layer
|
||||||
|
- [ ] add event normalization types
|
||||||
|
- [ ] add listener orchestration
|
||||||
|
- [ ] add tests
|
||||||
59
khbb_lib/src/app.rs
Normal file
59
khbb_lib/src/app.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// file: khbb_lib/src/app.rs
|
||||||
|
|
||||||
|
/// Runs the initial listener application workflow.
|
||||||
|
///
|
||||||
|
/// This first version only:
|
||||||
|
/// - loads configuration
|
||||||
|
/// - opens the SQLite connection pool
|
||||||
|
/// - verifies connectivity
|
||||||
|
/// - keeps the runtime alive as the future integration point for listener tasks
|
||||||
|
pub async fn run_listener_app(config_path: &str) -> core::result::Result<(), crate::KhbbError> {
|
||||||
|
let config_result = crate::KhbbAppConfig::load_from_json_file(config_path).await;
|
||||||
|
let config = match config_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => {
|
||||||
|
return Err(error);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let tracing_result = crate::init_tracing(&config.log_filter);
|
||||||
|
match tracing_result {
|
||||||
|
Ok(()) => {},
|
||||||
|
Err(error) => {
|
||||||
|
return Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tracing::info!(
|
||||||
|
database_url = %config.database_url,
|
||||||
|
solana_http_rpc_url = %config.solana_http_rpc_url,
|
||||||
|
solana_ws_rpc_url = %config.solana_ws_rpc_url,
|
||||||
|
yellowstone_grpc_url = ?config.yellowstone_grpc_url,
|
||||||
|
"khbb listener app starting"
|
||||||
|
);
|
||||||
|
let connect_result = sqlx::sqlite::SqlitePoolOptions::new()
|
||||||
|
.max_connections(1)
|
||||||
|
.connect(&config.database_url)
|
||||||
|
.await;
|
||||||
|
let pool = match connect_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => {
|
||||||
|
return Err(crate::KhbbError::Database {
|
||||||
|
context: "connect sqlite pool",
|
||||||
|
message: error.to_string(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let ping_result = sqlx::query("SELECT 1;").execute(&pool).await;
|
||||||
|
match ping_result {
|
||||||
|
Ok(_) => {
|
||||||
|
tracing::info!("sqlite connectivity check succeeded");
|
||||||
|
},
|
||||||
|
Err(error) => {
|
||||||
|
return Err(crate::KhbbError::Database {
|
||||||
|
context: "ping sqlite database",
|
||||||
|
message: error.to_string(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tracing::info!("listener tasks are not wired yet");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
71
khbb_lib/src/config.rs
Normal file
71
khbb_lib/src/config.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// file: khbb_lib/src/config.rs
|
||||||
|
|
||||||
|
/// Root application configuration used by the initial listener stack.
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct KhbbAppConfig {
|
||||||
|
/// Path or URL to the SQLite database.
|
||||||
|
pub database_url: std::string::String,
|
||||||
|
/// Solana HTTP RPC endpoint.
|
||||||
|
pub solana_http_rpc_url: std::string::String,
|
||||||
|
/// Solana WebSocket RPC endpoint.
|
||||||
|
pub solana_ws_rpc_url: std::string::String,
|
||||||
|
/// Optional Yellowstone gRPC endpoint.
|
||||||
|
pub yellowstone_grpc_url: std::option::Option<std::string::String>,
|
||||||
|
/// Tracing filter string.
|
||||||
|
pub log_filter: std::string::String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KhbbAppConfig {
|
||||||
|
/// Loads the application configuration from a JSON file.
|
||||||
|
pub async fn load_from_json_file(path: &str) -> core::result::Result<Self, crate::KhbbError> {
|
||||||
|
let file_content_result = tokio::fs::read_to_string(path).await;
|
||||||
|
let file_content = match file_content_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => {
|
||||||
|
return Err(crate::KhbbError::Io {
|
||||||
|
context: "read config file",
|
||||||
|
message: error.to_string(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let parse_result = serde_json::from_str::<Self>(&file_content);
|
||||||
|
let config = match parse_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => {
|
||||||
|
return Err(crate::KhbbError::Json {
|
||||||
|
context: "parse config json",
|
||||||
|
message: error.to_string(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let validate_result = config.validate();
|
||||||
|
match validate_result {
|
||||||
|
Ok(()) => Ok(config),
|
||||||
|
Err(error) => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Validates the application configuration.
|
||||||
|
pub fn validate(&self) -> core::result::Result<(), crate::KhbbError> {
|
||||||
|
if self.database_url.trim().is_empty() {
|
||||||
|
return Err(crate::KhbbError::Config {
|
||||||
|
message: std::string::String::from("database_url must not be empty"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if self.solana_http_rpc_url.trim().is_empty() {
|
||||||
|
return Err(crate::KhbbError::Config {
|
||||||
|
message: std::string::String::from("solana_http_rpc_url must not be empty"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if self.solana_ws_rpc_url.trim().is_empty() {
|
||||||
|
return Err(crate::KhbbError::Config {
|
||||||
|
message: std::string::String::from("solana_ws_rpc_url must not be empty"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if self.log_filter.trim().is_empty() {
|
||||||
|
return Err(crate::KhbbError::Config {
|
||||||
|
message: std::string::String::from("log_filter must not be empty"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
76
khbb_lib/src/error.rs
Normal file
76
khbb_lib/src/error.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// file: khbb_lib/src/error.rs
|
||||||
|
|
||||||
|
/// Main error type used across the khbb workspace.
|
||||||
|
///
|
||||||
|
/// This project intentionally uses a single explicit error enum instead of
|
||||||
|
/// `anyhow` or `thiserror`.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum KhbbError {
|
||||||
|
/// Returned when a filesystem operation fails.
|
||||||
|
Io {
|
||||||
|
/// Human-readable operation label.
|
||||||
|
context: &'static str,
|
||||||
|
/// Source message.
|
||||||
|
message: std::string::String,
|
||||||
|
},
|
||||||
|
/// Returned when JSON decoding or encoding fails.
|
||||||
|
Json {
|
||||||
|
/// Human-readable operation label.
|
||||||
|
context: &'static str,
|
||||||
|
/// Source message.
|
||||||
|
message: std::string::String,
|
||||||
|
},
|
||||||
|
/// Returned when configuration validation fails.
|
||||||
|
Config {
|
||||||
|
/// Validation message.
|
||||||
|
message: std::string::String,
|
||||||
|
},
|
||||||
|
/// Returned when SQLx operations fail.
|
||||||
|
Database {
|
||||||
|
/// Human-readable operation label.
|
||||||
|
context: &'static str,
|
||||||
|
/// Source message.
|
||||||
|
message: std::string::String,
|
||||||
|
},
|
||||||
|
/// Returned when tracing initialization fails.
|
||||||
|
Tracing {
|
||||||
|
/// Human-readable operation label.
|
||||||
|
context: &'static str,
|
||||||
|
/// Source message.
|
||||||
|
message: std::string::String,
|
||||||
|
},
|
||||||
|
/// Returned when a runtime task fails.
|
||||||
|
Runtime {
|
||||||
|
/// Human-readable operation label.
|
||||||
|
context: &'static str,
|
||||||
|
/// Source message.
|
||||||
|
message: std::string::String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for KhbbError {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Io { context, message } => {
|
||||||
|
write!(f, "io error during {context}: {message}")
|
||||||
|
},
|
||||||
|
Self::Json { context, message } => {
|
||||||
|
write!(f, "json error during {context}: {message}")
|
||||||
|
},
|
||||||
|
Self::Config { message } => {
|
||||||
|
write!(f, "configuration error: {message}")
|
||||||
|
},
|
||||||
|
Self::Database { context, message } => {
|
||||||
|
write!(f, "database error during {context}: {message}")
|
||||||
|
},
|
||||||
|
Self::Tracing { context, message } => {
|
||||||
|
write!(f, "tracing error during {context}: {message}")
|
||||||
|
},
|
||||||
|
Self::Runtime { context, message } => {
|
||||||
|
write!(f, "runtime error during {context}: {message}")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for KhbbError {}
|
||||||
23
khbb_lib/src/lib.rs
Normal file
23
khbb_lib/src/lib.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// file: khbb_lib/src/lib.rs
|
||||||
|
|
||||||
|
//! Core public library for the `khadhroony-bobot` workspace.
|
||||||
|
//!
|
||||||
|
//! This crate exposes the reusable building blocks shared by the khbb
|
||||||
|
//! applications, starting with the listener runtime bootstrap.
|
||||||
|
|
||||||
|
#![deny(unreachable_pub)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
mod app;
|
||||||
|
mod config;
|
||||||
|
mod error;
|
||||||
|
mod tracing_setup;
|
||||||
|
|
||||||
|
/// Public re-exports for the khbb core library.
|
||||||
|
pub use crate::app::run_listener_app;
|
||||||
|
/// Public re-exports for configuration loading.
|
||||||
|
pub use crate::config::KhbbAppConfig;
|
||||||
|
/// Public re-exports for configuration loading errors and runtime errors.
|
||||||
|
pub use crate::error::KhbbError;
|
||||||
|
/// Public re-exports for tracing initialization.
|
||||||
|
pub use crate::tracing_setup::init_tracing;
|
||||||
31
khbb_lib/src/tracing_setup.rs
Normal file
31
khbb_lib/src/tracing_setup.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// file: khbb_lib/src/tracing_setup.rs
|
||||||
|
|
||||||
|
/// Initializes tracing subscribers for the application.
|
||||||
|
pub fn init_tracing(log_filter: &str) -> core::result::Result<(), crate::KhbbError> {
|
||||||
|
let env_filter_result =
|
||||||
|
tracing_subscriber::EnvFilter::try_new(std::string::String::from(log_filter));
|
||||||
|
let env_filter = match env_filter_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => {
|
||||||
|
return Err(crate::KhbbError::Tracing {
|
||||||
|
context: "build env filter",
|
||||||
|
message: error.to_string(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let subscriber = tracing_subscriber::fmt()
|
||||||
|
.with_env_filter(env_filter)
|
||||||
|
.with_target(true)
|
||||||
|
.with_thread_ids(true)
|
||||||
|
.with_thread_names(true)
|
||||||
|
.with_ansi(true)
|
||||||
|
.finish();
|
||||||
|
let set_result = tracing::subscriber::set_global_default(subscriber);
|
||||||
|
match set_result {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(error) => Err(crate::KhbbError::Tracing {
|
||||||
|
context: "set global subscriber",
|
||||||
|
message: error.to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
14
khbb_listener_app/Cargo.toml
Normal file
14
khbb_listener_app/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# file: khbb_listener_app/Cargo.toml
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "khbb_listener_app"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
khbb_lib = { path = "../khbb_lib" }
|
||||||
|
tokio.workspace = true
|
||||||
|
tracing.workspace = true
|
||||||
15
khbb_listener_app/README.md
Normal file
15
khbb_listener_app/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!-- file: khbb_listener_app/README.md -->
|
||||||
|
|
||||||
|
# khbb_listener_app
|
||||||
|
|
||||||
|
Listener binary for the `khadhroony-bobot` workspace.
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
This binary starts the listener runtime and delegates all logic to `khbb_lib`.
|
||||||
|
|
||||||
|
## Current state
|
||||||
|
|
||||||
|
- reads `config.json` by default
|
||||||
|
- accepts an optional config path as first CLI argument
|
||||||
|
- initializes the runtime through `khbb_lib`
|
||||||
9
khbb_listener_app/TODO.md
Normal file
9
khbb_listener_app/TODO.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!-- file: khbb_listener_app/TODO.md -->
|
||||||
|
|
||||||
|
# khbb_listener_app TODO
|
||||||
|
|
||||||
|
- [x] create thin binary entrypoint
|
||||||
|
- [x] delegate startup to `khbb_lib`
|
||||||
|
- [ ] add clean shutdown handling
|
||||||
|
- [ ] add signal handling
|
||||||
|
- [ ] add richer CLI options later if needed
|
||||||
26
khbb_listener_app/src/main.rs
Normal file
26
khbb_listener_app/src/main.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// file: khbb_listener_app/src/main.rs
|
||||||
|
|
||||||
|
//! Binary entrypoint for the khbb listener application.
|
||||||
|
//!
|
||||||
|
//! This binary remains intentionally thin and delegates its logic to `khbb_lib`.
|
||||||
|
|
||||||
|
#![deny(unreachable_pub)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
/// Entrypoint of the khbb listener binary.
|
||||||
|
///
|
||||||
|
/// This binary is intentionally thin and delegates all business logic to
|
||||||
|
/// `khbb_lib`.
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> std::process::ExitCode {
|
||||||
|
let args = std::env::args().collect::<std::vec::Vec<std::string::String>>();
|
||||||
|
let config_path = if args.len() >= 2 { args[1].as_str() } else { "config.json" };
|
||||||
|
let run_result = khbb_lib::run_listener_app(config_path).await;
|
||||||
|
match run_result {
|
||||||
|
Ok(()) => std::process::ExitCode::SUCCESS,
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("khbb_listener_app failed: {error}");
|
||||||
|
std::process::ExitCode::FAILURE
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
38
rustfmt.toml
Normal file
38
rustfmt.toml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# file: rustfmt.toml
|
||||||
|
|
||||||
|
edition = "2024"
|
||||||
|
newline_style = "Unix"
|
||||||
|
use_small_heuristics = "Default"
|
||||||
|
hard_tabs = false
|
||||||
|
tab_spaces = 4
|
||||||
|
max_width = 100
|
||||||
|
chain_width = 80
|
||||||
|
fn_call_width = 80
|
||||||
|
attr_fn_like_width = 80
|
||||||
|
struct_lit_width = 40
|
||||||
|
struct_variant_width = 40
|
||||||
|
array_width = 80
|
||||||
|
single_line_if_else_max_width = 80
|
||||||
|
single_line_let_else_max_width = 80
|
||||||
|
imports_indent = "Block"
|
||||||
|
group_imports = "StdExternalCrate"
|
||||||
|
imports_granularity = "Module"
|
||||||
|
reorder_imports = true
|
||||||
|
reorder_modules = true
|
||||||
|
normalize_comments = false
|
||||||
|
normalize_doc_attributes = false
|
||||||
|
format_code_in_doc_comments = false
|
||||||
|
wrap_comments = false
|
||||||
|
format_strings = false
|
||||||
|
hex_literal_case = "Lower"
|
||||||
|
empty_item_single_line = true
|
||||||
|
struct_field_align_threshold = 0
|
||||||
|
enum_discrim_align_threshold = 0
|
||||||
|
match_arm_blocks = true
|
||||||
|
match_block_trailing_comma = true
|
||||||
|
trailing_comma = "Vertical"
|
||||||
|
use_field_init_shorthand = true
|
||||||
|
use_try_shorthand = false
|
||||||
|
force_explicit_abi = true
|
||||||
|
condense_wildcard_suffixes = false
|
||||||
|
unstable_features = false
|
||||||
Reference in New Issue
Block a user