0.5.1-pre.002
This commit is contained in:
31
ks-core/CHANGELOG.md
Normal file
31
ks-core/CHANGELOG.md
Normal file
@@ -0,0 +1,31 @@
|
||||
<!-- file: ks-core/CHANGELOG.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# CHANGELOG — ks-core
|
||||
|
||||
## `0.5.1-pre.002`
|
||||
|
||||
- renomme le package et le répertoire `kb-core` en `ks-core` et l'identifiant Rust `kb_core` en `ks_core` ;
|
||||
- met à jour ses consommateurs, chemins documentaires et tests sans changer les primitives d'erreur partagées.
|
||||
|
||||
## 0.4.6
|
||||
|
||||
- alignement de la crate sur la version fonctionnelle bot3 `0.4.6` ;
|
||||
- clôture des tâches de migration applicables et report explicite des évolutions ultérieures dans le TODO.
|
||||
|
||||
## 0.1.0-pre.072
|
||||
|
||||
- reclassement du TODO selon les blocants avant `0.4.6`, les travaux `0.4.7`, les versions ultérieures et les dépendances conditionnelles.
|
||||
|
||||
## 0.1.0-pre.071
|
||||
|
||||
- réécriture du README pour l’architecture bot3 ;
|
||||
- ajout du TODO, du guide d’utilisation et du changelog de crate ;
|
||||
- documentation des erreurs partagées et des identités de modules ;
|
||||
- ajout d’exemples couvrant les familles d’API publiques.
|
||||
|
||||
## 0.1.0-pre.062
|
||||
|
||||
- migration des primitives communes vers la crate consolidée `ks-core` ;
|
||||
- maintien d’un type d’erreur explicite sans `anyhow` ni `thiserror` ;
|
||||
- adaptation aux normes Rust 2024 et Khadhroony bot3.
|
||||
12
ks-core/Cargo.toml
Normal file
12
ks-core/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
# file: ks-core/Cargo.toml
|
||||
# version: 2
|
||||
|
||||
[package]
|
||||
name = "ks-core"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
37
ks-core/README.md
Normal file
37
ks-core/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
<!-- file: ks-core/README.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# ks-core
|
||||
|
||||
`ks-core` fournit les primitives minimales partagées par l’ensemble de `khadhroony-bot3`.
|
||||
|
||||
## Responsabilités
|
||||
|
||||
- type d’erreur explicite commun au workspace ;
|
||||
- alias `Result<T>` ;
|
||||
- identité stable des modules ;
|
||||
- classification des grandes familles de traitement.
|
||||
|
||||
La crate reste volontairement petite et ne dépend d’aucune couche fonctionnelle supérieure.
|
||||
|
||||
## API publique
|
||||
|
||||
- `Error` ;
|
||||
- `Result<T>` ;
|
||||
- `ModuleName` ;
|
||||
- `ModuleVersion` ;
|
||||
- `ModuleKind`.
|
||||
|
||||
Voir [USAGE.md](USAGE.md) pour les exemples.
|
||||
|
||||
## Relations
|
||||
|
||||
`ks-core` est utilisée par toutes les crates qui ont besoin d’un contrat d’erreur ou d’une identité de module partagée. Elle ne contient ni configuration, ni transport, ni stockage, ni logique Solana.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [USAGE.md](USAGE.md)
|
||||
- [TODO.md](TODO.md)
|
||||
- [CHANGELOG.md](CHANGELOG.md)
|
||||
- [Architecture générale](../docs/architecture/ARCHITECTURE.md)
|
||||
- [Carte des crates](../docs/architecture/CRATE_MAP.md)
|
||||
9
ks-core/TODO.md
Normal file
9
ks-core/TODO.md
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- file: ks-core/TODO.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# TODO — ks-core
|
||||
|
||||
## Versions ultérieures
|
||||
|
||||
- [ ] Dette technique - réévaluer les variantes génériques de `Error` lorsque les frontières de domaine seront stabilisées.
|
||||
- [ ] Dette technique - remplacer les usages de `Error::Custom` qui méritent une famille publique dédiée.
|
||||
108
ks-core/USAGE.md
Normal file
108
ks-core/USAGE.md
Normal file
@@ -0,0 +1,108 @@
|
||||
<!-- file: ks-core/USAGE.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Utilisation de ks-core
|
||||
|
||||
## Objectif
|
||||
|
||||
La crate expose les contrats minimaux communs utilisés par les autres crates du workspace.
|
||||
|
||||
## Résultat partagé
|
||||
|
||||
```rust
|
||||
fn validate_name(name: &str) -> ks_core::Result<()> {
|
||||
if name.trim().is_empty() {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"name_empty",
|
||||
"name must not be empty",
|
||||
));
|
||||
}
|
||||
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
```
|
||||
|
||||
## Erreur personnalisée stable
|
||||
|
||||
```rust
|
||||
let error = ks_core::Error::new(
|
||||
"profile_missing",
|
||||
"the requested profile does not exist",
|
||||
);
|
||||
|
||||
assert_eq!(error.code(), "profile_missing");
|
||||
assert_eq!(
|
||||
error.message(),
|
||||
"the requested profile does not exist"
|
||||
);
|
||||
```
|
||||
|
||||
`Error::new` doit recevoir un code stable destiné aux logs, aux tests et aux adaptateurs UI.
|
||||
|
||||
## Familles d’erreur
|
||||
|
||||
```rust
|
||||
let config_error = ks_core::Error::config("missing active profile");
|
||||
let io_error = ks_core::Error::io("cannot read configuration file");
|
||||
let db_error = ks_core::Error::db("database connection refused");
|
||||
|
||||
assert_eq!(config_error.code(), "config");
|
||||
assert_eq!(io_error.code(), "io");
|
||||
assert_eq!(db_error.code(), "db");
|
||||
```
|
||||
|
||||
Les constructeurs publics disponibles couvrent notamment la configuration, les I/O, JSON, tracing, Tauri, HTTP, WebSocket, base de données, état invalide, absence de connexion et fonctionnalité non implémentée.
|
||||
|
||||
## Conversion depuis une erreur I/O
|
||||
|
||||
```rust
|
||||
let read_result = std::fs::read_to_string("missing.file");
|
||||
|
||||
let core_result: ks_core::Result<std::string::String> =
|
||||
match read_result {
|
||||
std::result::Result::Ok(value) => {
|
||||
std::result::Result::Ok(value)
|
||||
},
|
||||
std::result::Result::Err(error) => {
|
||||
std::result::Result::Err(ks_core::Error::from(error))
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Identité d’un module
|
||||
|
||||
```rust
|
||||
let name = ks_core::ModuleName(
|
||||
"spl_token_decoder".to_string(),
|
||||
);
|
||||
let version = ks_core::ModuleVersion(
|
||||
"0.4.6".to_string(),
|
||||
);
|
||||
let kind = ks_core::ModuleKind::Decoder;
|
||||
|
||||
assert_eq!(name.0, "spl_token_decoder");
|
||||
assert_eq!(version.0, "0.4.6");
|
||||
assert_eq!(kind, ks_core::ModuleKind::Decoder);
|
||||
```
|
||||
|
||||
`ModuleKind` distingue actuellement les ingestors, extractors, observers, decoders, materializers, aggregators et validators.
|
||||
|
||||
## Erreurs et invariants
|
||||
|
||||
- le code d’une erreur doit rester stable ;
|
||||
- le message peut être détaillé pour l’opérateur, mais ne doit pas contenir de secret ;
|
||||
- `ModuleName` et `ModuleVersion` sont des identités, pas des mécanismes de résolution dynamique ;
|
||||
- une nouvelle variante publique doit rester compatible avec les consommateurs du workspace.
|
||||
|
||||
## Tests de référence
|
||||
|
||||
- `custom_error_preserves_code_and_message` ;
|
||||
- `family_error_formats_with_family_prefix`.
|
||||
|
||||
Ces tests illustrent le contrat stable entre code, message et représentation textuelle.
|
||||
|
||||
## Limites durables
|
||||
|
||||
- `ks-core` ne remplace pas les types métier spécialisés ;
|
||||
- elle ne fournit pas de journalisation ni de sérialisation automatique des erreurs ;
|
||||
- elle ne contient pas de logique de transport, stockage ou protocole.
|
||||
197
ks-core/src/error.rs
Normal file
197
ks-core/src/error.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
// file: ks-core/src/error.rs
|
||||
// version: 7
|
||||
|
||||
//! Error and result primitives shared by the workspace.
|
||||
|
||||
/// Workspace-wide result alias.
|
||||
pub type Result<T> = std::result::Result<T, crate::Error>;
|
||||
|
||||
/// Workspace-wide explicit error type.
|
||||
///
|
||||
/// The project avoids generic catch-all error crates, so this enum centralizes
|
||||
/// the first stable error families used by core, configuration, logging,
|
||||
/// applications, RPC, stores and execution modules.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Error {
|
||||
/// Configuration or profile validation error.
|
||||
Config(std::string::String),
|
||||
/// Filesystem or standard I/O error.
|
||||
Io(std::string::String),
|
||||
/// JSON serialization, deserialization or schema validation error.
|
||||
Json(std::string::String),
|
||||
/// Tracing initialization or logging runtime error.
|
||||
Tracing(std::string::String),
|
||||
/// Tauri application or WebView runtime error.
|
||||
Tauri(std::string::String),
|
||||
/// HTTP transport error.
|
||||
Http(std::string::String),
|
||||
/// WebSocket transport error.
|
||||
Ws(std::string::String),
|
||||
/// Database or storage backend error.
|
||||
Db(std::string::String),
|
||||
/// Invalid internal state error.
|
||||
InvalidState(std::string::String),
|
||||
/// Operation requested while a client or subsystem is not connected.
|
||||
NotConnected(std::string::String),
|
||||
/// Feature intentionally scheduled for a later version.
|
||||
NotImplemented(std::string::String),
|
||||
/// Stable custom error code used while domains are still being split.
|
||||
Custom {
|
||||
/// Stable custom error code.
|
||||
code: std::string::String,
|
||||
/// Human-readable custom error message.
|
||||
message: std::string::String,
|
||||
},
|
||||
}
|
||||
|
||||
impl crate::Error {
|
||||
/// Creates a custom error value with a stable code.
|
||||
pub fn new(
|
||||
code: impl std::convert::Into<std::string::String>,
|
||||
message: impl std::convert::Into<std::string::String>,
|
||||
) -> Self {
|
||||
return Self::Custom {
|
||||
code: code.into(),
|
||||
message: message.into(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a configuration error value.
|
||||
pub fn config(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::Config(message.into());
|
||||
}
|
||||
|
||||
/// Creates an I/O error value.
|
||||
pub fn io(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::Io(message.into());
|
||||
}
|
||||
|
||||
/// Creates a JSON error value.
|
||||
pub fn json(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::Json(message.into());
|
||||
}
|
||||
|
||||
/// Creates a tracing error value.
|
||||
pub fn tracing(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::Tracing(message.into());
|
||||
}
|
||||
|
||||
/// Creates a Tauri runtime error value.
|
||||
pub fn tauri(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::Tauri(message.into());
|
||||
}
|
||||
|
||||
/// Creates an HTTP transport error value.
|
||||
pub fn http(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::Http(message.into());
|
||||
}
|
||||
|
||||
/// Creates a WebSocket transport error value.
|
||||
pub fn ws(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::Ws(message.into());
|
||||
}
|
||||
|
||||
/// Creates a database error value.
|
||||
pub fn db(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::Db(message.into());
|
||||
}
|
||||
|
||||
/// Creates an invalid state error value.
|
||||
pub fn invalid_state(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::InvalidState(message.into());
|
||||
}
|
||||
|
||||
/// Creates a not-connected error value.
|
||||
pub fn not_connected(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::NotConnected(message.into());
|
||||
}
|
||||
|
||||
/// Creates a not-implemented error value.
|
||||
pub fn not_implemented(message: impl std::convert::Into<std::string::String>) -> Self {
|
||||
return Self::NotImplemented(message.into());
|
||||
}
|
||||
|
||||
/// Returns a stable code for logs and UI diagnostics.
|
||||
pub fn code(&self) -> &str {
|
||||
return match self {
|
||||
Self::Config(_) => "config",
|
||||
Self::Io(_) => "io",
|
||||
Self::Json(_) => "json",
|
||||
Self::Tracing(_) => "tracing",
|
||||
Self::Tauri(_) => "tauri",
|
||||
Self::Http(_) => "http",
|
||||
Self::Ws(_) => "ws",
|
||||
Self::Db(_) => "db",
|
||||
Self::InvalidState(_) => "invalid_state",
|
||||
Self::NotConnected(_) => "not_connected",
|
||||
Self::NotImplemented(_) => "not_implemented",
|
||||
Self::Custom { code, message: _ } => code.as_str(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the human-readable message without the family prefix.
|
||||
pub fn message(&self) -> &str {
|
||||
return match self {
|
||||
Self::Config(message) => message,
|
||||
Self::Io(message) => message,
|
||||
Self::Json(message) => message,
|
||||
Self::Tracing(message) => message,
|
||||
Self::Tauri(message) => message,
|
||||
Self::Http(message) => message,
|
||||
Self::Ws(message) => message,
|
||||
Self::Db(message) => message,
|
||||
Self::InvalidState(message) => message,
|
||||
Self::NotConnected(message) => message,
|
||||
Self::NotImplemented(message) => message,
|
||||
Self::Custom { code: _, message } => message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for crate::Error {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return match self {
|
||||
Self::Config(message) => write!(formatter, "configuration error: {message}"),
|
||||
Self::Io(message) => write!(formatter, "io error: {message}"),
|
||||
Self::Json(message) => write!(formatter, "json error: {message}"),
|
||||
Self::Tracing(message) => write!(formatter, "tracing error: {message}"),
|
||||
Self::Tauri(message) => write!(formatter, "tauri error: {message}"),
|
||||
Self::Http(message) => write!(formatter, "http error: {message}"),
|
||||
Self::Ws(message) => write!(formatter, "websocket error: {message}"),
|
||||
Self::Db(message) => write!(formatter, "database error: {message}"),
|
||||
Self::InvalidState(message) => write!(formatter, "invalid state: {message}"),
|
||||
Self::NotConnected(message) => write!(formatter, "not connected: {message}"),
|
||||
Self::NotImplemented(message) => {
|
||||
write!(formatter, "not implemented: {message}")
|
||||
},
|
||||
Self::Custom { code, message } => write!(formatter, "{code}: {message}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for crate::Error {}
|
||||
|
||||
impl std::convert::From<std::io::Error> for crate::Error {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
return Self::Io(error.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn custom_error_preserves_code_and_message() {
|
||||
let error = super::Error::new("sample_code", "sample message");
|
||||
assert_eq!(error.code(), "sample_code");
|
||||
assert_eq!(error.message(), "sample message");
|
||||
assert_eq!(error.to_string(), "sample_code: sample message");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn family_error_formats_with_family_prefix() {
|
||||
let error = super::Error::config("missing profile");
|
||||
assert_eq!(error.code(), "config");
|
||||
assert_eq!(error.message(), "missing profile");
|
||||
assert_eq!(error.to_string(), "configuration error: missing profile");
|
||||
}
|
||||
}
|
||||
21
ks-core/src/lib.rs
Normal file
21
ks-core/src/lib.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
// file: ks-core/src/lib.rs
|
||||
// version: 3
|
||||
|
||||
//! Core primitives, module identity and shared errors for the workspace.
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod error;
|
||||
mod module;
|
||||
|
||||
/// Workspace-wide explicit error type.
|
||||
pub use self::error::Error;
|
||||
/// Workspace-wide result alias.
|
||||
pub use self::error::Result;
|
||||
/// Processing module category.
|
||||
pub use self::module::ModuleKind;
|
||||
/// Stable module name.
|
||||
pub use self::module::ModuleName;
|
||||
/// Stable semantic module version.
|
||||
pub use self::module::ModuleVersion;
|
||||
31
ks-core/src/module.rs
Normal file
31
ks-core/src/module.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
// file: ks-core/src/module.rs
|
||||
// version: 2
|
||||
|
||||
//! Module identity and module-kind primitives.
|
||||
|
||||
/// Stable module name.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ModuleName(pub std::string::String);
|
||||
|
||||
/// Stable semantic module version.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ModuleVersion(pub std::string::String);
|
||||
|
||||
/// Processing module category.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ModuleKind {
|
||||
/// Raw transaction ingestion.
|
||||
Ingestor,
|
||||
/// Generic Solana extractor.
|
||||
Extractor,
|
||||
/// Program observation builder.
|
||||
Observer,
|
||||
/// Protocol decoder.
|
||||
Decoder,
|
||||
/// Business materializer.
|
||||
Materializer,
|
||||
/// Aggregation stage.
|
||||
Aggregator,
|
||||
/// Validation stage.
|
||||
Validator,
|
||||
}
|
||||
Reference in New Issue
Block a user