use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ReplContext {
symbols: HashMap<String, SymbolInfo>,
imports: HashMap<String, Vec<String>>,
roots: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct SymbolInfo {
pub name: String,
pub kind: String,
pub dependencies: Vec<String>,
pub is_public: bool,
pub type_sig: Option<String>,
}
impl Default for ReplContext {
fn default() -> Self {
Self::new()
}
}
impl ReplContext {
pub fn new() -> Self {
Self {
symbols: HashMap::new(),
imports: HashMap::new(),
roots: Vec::new(),
}
}
pub fn add_declaration(&mut self, name: &str, kind: &str) {
let info = SymbolInfo {
name: name.to_string(),
kind: kind.to_string(),
dependencies: Vec::new(),
is_public: true, type_sig: None,
};
self.symbols.insert(name.to_string(), info);
if !self.roots.contains(&name.to_string()) {
self.roots.push(name.to_string());
}
}
pub fn add_dependency(&mut self, from: &str, to: &str) {
self.imports
.entry(from.to_string())
.or_default()
.push(to.to_string());
if let Some(info) = self.symbols.get_mut(from) {
if !info.dependencies.contains(&to.to_string()) {
info.dependencies.push(to.to_string());
}
}
}
pub fn get_symbol(&self, name: &str) -> Option<&SymbolInfo> {
self.symbols.get(name)
}
pub fn symbol_names(&self) -> Vec<&str> {
self.symbols.keys().map(|s| s.as_str()).collect()
}
pub fn roots(&self) -> &[String] {
&self.roots
}
pub fn remove_symbol(&mut self, name: &str) {
self.symbols.remove(name);
self.imports.remove(name);
self.roots.retain(|r| r != name);
}
pub fn clear(&mut self) {
self.symbols.clear();
self.imports.clear();
self.roots.clear();
}
pub fn has_symbol(&self, name: &str) -> bool {
self.symbols.contains_key(name)
}
pub fn get_dependencies(&self, name: &str) -> Option<&Vec<String>> {
self.imports.get(name)
}
pub fn set_type_sig(&mut self, name: &str, sig: &str) {
if let Some(info) = self.symbols.get_mut(name) {
info.type_sig = Some(sig.to_string());
}
}
pub fn len(&self) -> usize {
self.symbols.len()
}
pub fn is_empty(&self) -> bool {
self.symbols.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_context_new() {
let ctx = ReplContext::new();
assert!(ctx.is_empty());
assert!(ctx.roots().is_empty());
}
#[test]
fn test_add_declaration() {
let mut ctx = ReplContext::new();
ctx.add_declaration("Point", "gene");
assert!(ctx.has_symbol("Point"));
assert_eq!(ctx.len(), 1);
let info = ctx.get_symbol("Point").unwrap();
assert_eq!(info.name, "Point");
assert_eq!(info.kind, "gene");
}
#[test]
fn test_add_dependency() {
let mut ctx = ReplContext::new();
ctx.add_declaration("Point", "gene");
ctx.add_declaration("Circle", "gene");
ctx.add_dependency("Circle", "Point");
let deps = ctx.get_dependencies("Circle").unwrap();
assert!(deps.contains(&"Point".to_string()));
}
#[test]
fn test_remove_symbol() {
let mut ctx = ReplContext::new();
ctx.add_declaration("Point", "gene");
assert!(ctx.has_symbol("Point"));
ctx.remove_symbol("Point");
assert!(!ctx.has_symbol("Point"));
}
#[test]
fn test_clear() {
let mut ctx = ReplContext::new();
ctx.add_declaration("Point", "gene");
ctx.add_declaration("Circle", "gene");
ctx.clear();
assert!(ctx.is_empty());
}
}