use std::fmt;
#[cfg(feature = "serde-extras")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-extras", derive(Serialize, Deserialize))]
pub enum Paradigm {
ObjectOriented,
Functional,
Reactive,
Procedural,
Mixed,
}
impl Paradigm {
pub fn short_name(&self) -> &'static str {
match self {
Self::ObjectOriented => "OOP",
Self::Functional => "FP",
Self::Reactive => "Reactive",
Self::Procedural => "Procedural",
Self::Mixed => "Mixed",
}
}
pub fn full_name(&self) -> &'static str {
match self {
Self::ObjectOriented => "Object-Oriented Programming",
Self::Functional => "Functional Programming",
Self::Reactive => "Reactive Programming",
Self::Procedural => "Procedural Programming",
Self::Mixed => "Mixed Paradigm",
}
}
pub fn all_primary() -> &'static [Paradigm] {
&[
Paradigm::ObjectOriented,
Paradigm::Functional,
Paradigm::Reactive,
Paradigm::Procedural,
]
}
}
impl fmt::Display for Paradigm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.short_name())
}
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde-extras", derive(Serialize, Deserialize))]
pub struct ParadigmProfile {
pub oop_score: f64,
pub fp_score: f64,
pub reactive_score: f64,
pub procedural_score: f64,
pub indicators: Vec<ParadigmIndicator>,
pub total_tokens: usize,
pub match_count: usize,
}
impl ParadigmProfile {
pub fn new() -> Self {
Self::default()
}
pub fn dominant_paradigm(&self) -> Option<Paradigm> {
let threshold = 0.2; let margin = 0.1;
let scores = [
(Paradigm::ObjectOriented, self.oop_score),
(Paradigm::Functional, self.fp_score),
(Paradigm::Reactive, self.reactive_score),
(Paradigm::Procedural, self.procedural_score),
];
let mut sorted = scores;
sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
let (top_paradigm, top_score) = sorted[0];
let (_, second_score) = sorted[1];
if top_score < threshold {
None
} else if top_score - second_score < margin {
Some(Paradigm::Mixed)
} else {
Some(top_paradigm)
}
}
pub fn present_paradigms(&self, threshold: f64) -> Vec<(Paradigm, f64)> {
let mut result = Vec::new();
if self.oop_score >= threshold {
result.push((Paradigm::ObjectOriented, self.oop_score));
}
if self.fp_score >= threshold {
result.push((Paradigm::Functional, self.fp_score));
}
if self.reactive_score >= threshold {
result.push((Paradigm::Reactive, self.reactive_score));
}
if self.procedural_score >= threshold {
result.push((Paradigm::Procedural, self.procedural_score));
}
result.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
result
}
pub fn score(&self, paradigm: Paradigm) -> f64 {
match paradigm {
Paradigm::ObjectOriented => self.oop_score,
Paradigm::Functional => self.fp_score,
Paradigm::Reactive => self.reactive_score,
Paradigm::Procedural => self.procedural_score,
Paradigm::Mixed => {
let scores = [
self.oop_score,
self.fp_score,
self.reactive_score,
self.procedural_score,
];
let mean = scores.iter().sum::<f64>() / scores.len() as f64;
let variance: f64 =
scores.iter().map(|s| (s - mean).powi(2)).sum::<f64>() / scores.len() as f64;
1.0 - variance.sqrt() }
}
}
pub fn normalize(&mut self) {
let total = self.oop_score + self.fp_score + self.reactive_score + self.procedural_score;
if total > 0.0 {
self.oop_score /= total;
self.fp_score /= total;
self.reactive_score /= total;
self.procedural_score /= total;
}
}
pub fn normalized(&self) -> Self {
let mut copy = self.clone();
copy.normalize();
copy
}
pub fn merge(&mut self, other: &ParadigmProfile, weight: f64) {
let self_weight = 1.0 - weight;
self.oop_score = self.oop_score * self_weight + other.oop_score * weight;
self.fp_score = self.fp_score * self_weight + other.fp_score * weight;
self.reactive_score = self.reactive_score * self_weight + other.reactive_score * weight;
self.procedural_score =
self.procedural_score * self_weight + other.procedural_score * weight;
self.indicators.extend(other.indicators.iter().cloned());
self.total_tokens += other.total_tokens;
self.match_count += other.match_count;
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-extras", derive(Serialize, Deserialize))]
pub struct ParadigmIndicator {
pub paradigm: Paradigm,
pub category: IndicatorCategory,
pub pattern: String,
pub weight: f64,
pub position: Option<usize>,
pub length: usize,
pub is_strong: bool,
}
impl ParadigmIndicator {
pub fn new(
paradigm: Paradigm,
category: IndicatorCategory,
pattern: impl Into<String>,
weight: f64,
) -> Self {
Self {
paradigm,
category,
pattern: pattern.into(),
weight,
position: None,
length: 1,
is_strong: weight >= 0.7,
}
}
pub fn with_position(mut self, position: usize) -> Self {
self.position = Some(position);
self
}
pub fn with_length(mut self, length: usize) -> Self {
self.length = length;
self
}
pub fn with_strong(mut self, strong: bool) -> Self {
self.is_strong = strong;
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde-extras", derive(Serialize, Deserialize))]
pub enum IndicatorCategory {
OopClass,
OopInheritance,
OopEncapsulation,
OopPolymorphism,
OopInstantiation,
FpHigherOrder,
FpImmutability,
FpPurity,
FpPatternMatch,
FpAlgebraic,
FpRecursion,
ReactiveObservable,
ReactiveEvent,
ReactiveBackpressure,
ReactiveAsync,
ProceduralControlFlow,
ProceduralMutable,
ProceduralSideEffect,
ProceduralSequential,
}
impl IndicatorCategory {
pub fn paradigm(&self) -> Paradigm {
match self {
Self::OopClass
| Self::OopInheritance
| Self::OopEncapsulation
| Self::OopPolymorphism
| Self::OopInstantiation => Paradigm::ObjectOriented,
Self::FpHigherOrder
| Self::FpImmutability
| Self::FpPurity
| Self::FpPatternMatch
| Self::FpAlgebraic
| Self::FpRecursion => Paradigm::Functional,
Self::ReactiveObservable
| Self::ReactiveEvent
| Self::ReactiveBackpressure
| Self::ReactiveAsync => Paradigm::Reactive,
Self::ProceduralControlFlow
| Self::ProceduralMutable
| Self::ProceduralSideEffect
| Self::ProceduralSequential => Paradigm::Procedural,
}
}
pub fn short_name(&self) -> &'static str {
match self {
Self::OopClass => "class",
Self::OopInheritance => "inheritance",
Self::OopEncapsulation => "encapsulation",
Self::OopPolymorphism => "polymorphism",
Self::OopInstantiation => "instantiation",
Self::FpHigherOrder => "higher-order",
Self::FpImmutability => "immutability",
Self::FpPurity => "purity",
Self::FpPatternMatch => "pattern-match",
Self::FpAlgebraic => "algebraic",
Self::FpRecursion => "recursion",
Self::ReactiveObservable => "observable",
Self::ReactiveEvent => "event",
Self::ReactiveBackpressure => "backpressure",
Self::ReactiveAsync => "async",
Self::ProceduralControlFlow => "control-flow",
Self::ProceduralMutable => "mutable",
Self::ProceduralSideEffect => "side-effect",
Self::ProceduralSequential => "sequential",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-extras", derive(Serialize, Deserialize))]
pub enum OopIndicator {
ClassKeyword,
StructWithMethods,
InterfaceKeyword,
ExtendsKeyword,
ImplementsKeyword,
SuperReference,
SelfReference,
Constructor,
NewKeyword,
AccessModifier,
VirtualKeyword,
AbstractKeyword,
MethodDefinition,
PropertyDefinition,
GetterSetter,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-extras", derive(Serialize, Deserialize))]
pub enum FpIndicator {
Lambda,
Map,
Filter,
Reduce,
FlatMap,
Compose,
Curry,
ImmutableDecl,
PatternMatch,
OptionType,
ResultType,
Comprehension,
PureAnnotation,
TailRecursion,
MonadicBind,
FunctorOp,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-extras", derive(Serialize, Deserialize))]
pub enum ReactiveIndicator {
ObservableCreate,
Subscribe,
EventListener,
OnHandler,
Emit,
Stream,
FluxMono,
RxOperator,
Signal,
Effect,
AsyncStream,
Backpressure,
Scheduler,
HotCold,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-extras", derive(Serialize, Deserialize))]
pub enum ProceduralIndicator {
ForLoop,
WhileLoop,
MutableDecl,
Reassignment,
Goto,
BreakContinue,
ReturnStatement,
PrintStatement,
GlobalVariable,
VoidFunction,
PointerOps,
IncrementOps,
StatementBlock,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_paradigm_names() {
assert_eq!(Paradigm::ObjectOriented.short_name(), "OOP");
assert_eq!(Paradigm::Functional.short_name(), "FP");
assert_eq!(Paradigm::Reactive.short_name(), "Reactive");
assert_eq!(Paradigm::Procedural.short_name(), "Procedural");
}
#[test]
fn test_dominant_paradigm_clear_winner() {
let profile = ParadigmProfile {
oop_score: 0.8,
fp_score: 0.1,
reactive_score: 0.05,
procedural_score: 0.05,
..Default::default()
};
assert_eq!(profile.dominant_paradigm(), Some(Paradigm::ObjectOriented));
}
#[test]
fn test_dominant_paradigm_mixed() {
let profile = ParadigmProfile {
oop_score: 0.45,
fp_score: 0.40,
reactive_score: 0.1,
procedural_score: 0.05,
..Default::default()
};
assert_eq!(profile.dominant_paradigm(), Some(Paradigm::Mixed));
}
#[test]
fn test_dominant_paradigm_none() {
let profile = ParadigmProfile {
oop_score: 0.1,
fp_score: 0.05,
reactive_score: 0.05,
procedural_score: 0.05,
..Default::default()
};
assert_eq!(profile.dominant_paradigm(), None);
}
#[test]
fn test_present_paradigms() {
let profile = ParadigmProfile {
oop_score: 0.6,
fp_score: 0.3,
reactive_score: 0.1,
procedural_score: 0.05,
..Default::default()
};
let present = profile.present_paradigms(0.2);
assert_eq!(present.len(), 2);
assert_eq!(present[0].0, Paradigm::ObjectOriented);
assert_eq!(present[1].0, Paradigm::Functional);
}
#[test]
fn test_normalize() {
let mut profile = ParadigmProfile {
oop_score: 2.0,
fp_score: 1.0,
reactive_score: 0.5,
procedural_score: 0.5,
..Default::default()
};
profile.normalize();
assert!((profile.oop_score - 0.5).abs() < 0.001);
assert!((profile.fp_score - 0.25).abs() < 0.001);
assert!((profile.reactive_score - 0.125).abs() < 0.001);
assert!((profile.procedural_score - 0.125).abs() < 0.001);
}
#[test]
fn test_indicator_category_paradigm() {
assert_eq!(
IndicatorCategory::OopClass.paradigm(),
Paradigm::ObjectOriented
);
assert_eq!(
IndicatorCategory::FpHigherOrder.paradigm(),
Paradigm::Functional
);
assert_eq!(
IndicatorCategory::ReactiveObservable.paradigm(),
Paradigm::Reactive
);
assert_eq!(
IndicatorCategory::ProceduralControlFlow.paradigm(),
Paradigm::Procedural
);
}
#[test]
fn test_paradigm_indicator_creation() {
let indicator = ParadigmIndicator::new(
Paradigm::Functional,
IndicatorCategory::FpHigherOrder,
"map",
0.8,
)
.with_position(10)
.with_length(3);
assert_eq!(indicator.paradigm, Paradigm::Functional);
assert_eq!(indicator.pattern, "map");
assert_eq!(indicator.position, Some(10));
assert_eq!(indicator.length, 3);
assert!(indicator.is_strong);
}
}