0.5.1-pre.002

This commit is contained in:
2026-08-09 19:34:08 +02:00
parent 816eee59a9
commit 6a680767ae
767 changed files with 12257 additions and 12195 deletions

31
ks-core/CHANGELOG.md Normal file
View 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 larchitecture bot3 ;
- ajout du TODO, du guide dutilisation et du changelog de crate ;
- documentation des erreurs partagées et des identités de modules ;
- ajout dexemples couvrant les familles dAPI publiques.
## 0.1.0-pre.062
- migration des primitives communes vers la crate consolidée `ks-core` ;
- maintien dun type derreur explicite sans `anyhow` ni `thiserror` ;
- adaptation aux normes Rust 2024 et Khadhroony bot3.

12
ks-core/Cargo.toml Normal file
View 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
View File

@@ -0,0 +1,37 @@
<!-- file: ks-core/README.md -->
<!-- version: 5 -->
# ks-core
`ks-core` fournit les primitives minimales partagées par lensemble de `khadhroony-bot3`.
## Responsabilités
- type derreur 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 daucune 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 dun contrat derreur ou dune 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
View 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
View 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 derreur
```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é dun 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 dune erreur doit rester stable ;
- le message peut être détaillé pour lopé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
View 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
View 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
View 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,
}