libttl 0.1.1

A library for simulating TTL logic chips
Documentation
//! Defines the basic logic gates and the Gate trait.

use crate::logic_level::LogicLevel;
use std::fmt::Debug;

/// Trait for all logic gates.
///
/// This trait allows for gates to potentially have internal state,
/// although the basic gates implemented here (NOT, AND, OR, NAND) are stateless.
/// The `update` method simulates the gate's reaction to input changes,
/// and `get_output` retrieves the current output level.
pub trait Gate: Debug + Send + Sync {
    /// Returns the number of input pins for this gate.
    fn input_pin_count(&self) -> usize;

    /// Returns the number of output pins for this gate (usually 1).
    fn output_pin_count(&self) -> usize;

    /// Updates the gate's internal state (if any) and calculates the output
    /// based on the provided input levels.
    ///
    /// # Arguments
    /// * `inputs` - A slice containing the logic levels applied to the input pins.
    ///
    /// # Panics
    /// Panics if the length of `inputs` does not match `input_pin_count`.
    fn update(&mut self, inputs: &[LogicLevel]);

    /// Gets the current logic level of the specified output pin.
    ///
    /// # Arguments
    /// * `pin_index` - The index of the output pin (usually 0).
    ///
    /// # Panics
    /// Panics if `pin_index` is out of bounds for the gate's outputs.
    fn get_output(&self, pin_index: usize) -> LogicLevel;

    /// Creates a boxed clone of the gate. Necessary for cloning chips containing gates.
    fn box_clone(&self) -> Box<dyn Gate>;
}

// Implement Clone for Box<dyn Gate> using the box_clone method
impl Clone for Box<dyn Gate> {
    fn clone(&self) -> Self {
        self.box_clone()
    }
}

// --- Concrete Gate Implementations ---

// --- NOT Gate (Inverter) ---
#[derive(Debug, Clone, Copy, Default)]
pub struct NotGate {
    output: LogicLevel,
}

impl NotGate {
    pub fn new() -> Self {
        NotGate { output: LogicLevel::Low } // Default initial state
    }
}

impl Gate for NotGate {
    fn input_pin_count(&self) -> usize { 1 }
    fn output_pin_count(&self) -> usize { 1 }

    fn update(&mut self, inputs: &[LogicLevel]) {
        assert_eq!(inputs.len(), self.input_pin_count(), "Incorrect number of inputs for NOT gate");
        self.output = !inputs[0];
    }

    fn get_output(&self, pin_index: usize) -> LogicLevel {
        assert_eq!(pin_index, 0, "Invalid output pin index for NOT gate");
        self.output
    }

     fn box_clone(&self) -> Box<dyn Gate> {
        Box::new(*self)
    }
}

// --- AND Gate (2-Input) ---
#[derive(Debug, Clone, Copy, Default)]
pub struct AndGate {
     output: LogicLevel,
}

impl AndGate {
     pub fn new() -> Self {
        AndGate { output: LogicLevel::Low }
    }
}

impl Gate for AndGate {
    fn input_pin_count(&self) -> usize { 2 }
    fn output_pin_count(&self) -> usize { 1 }

    fn update(&mut self, inputs: &[LogicLevel]) {
        assert_eq!(inputs.len(), self.input_pin_count(), "Incorrect number of inputs for AND gate");
        self.output = crate::logic_level::and(inputs[0], inputs[1]);
    }

    fn get_output(&self, pin_index: usize) -> LogicLevel {
        assert_eq!(pin_index, 0, "Invalid output pin index for AND gate");
        self.output
    }

     fn box_clone(&self) -> Box<dyn Gate> {
        Box::new(*self)
    }
}

// --- OR Gate (2-Input) ---
#[derive(Debug, Clone, Copy, Default)]
pub struct OrGate {
     output: LogicLevel,
}

impl OrGate {
    pub fn new() -> Self {
        OrGate { output: LogicLevel::Low }
    }
}

impl Gate for OrGate {
    fn input_pin_count(&self) -> usize { 2 }
    fn output_pin_count(&self) -> usize { 1 }

    fn update(&mut self, inputs: &[LogicLevel]) {
        assert_eq!(inputs.len(), self.input_pin_count(), "Incorrect number of inputs for OR gate");
         self.output = crate::logic_level::or(inputs[0], inputs[1]);
    }

    fn get_output(&self, pin_index: usize) -> LogicLevel {
        assert_eq!(pin_index, 0, "Invalid output pin index for OR gate");
        self.output
    }

    fn box_clone(&self) -> Box<dyn Gate> {
        Box::new(*self)
    }
}

// --- NAND Gate (2-Input) ---
#[derive(Debug, Clone, Copy, Default)]
pub struct NandGate {
     output: LogicLevel,
}

impl NandGate {
    pub fn new() -> Self {
        NandGate { output: LogicLevel::High } // Default NAND output is High
    }
}

impl Gate for NandGate {
    fn input_pin_count(&self) -> usize { 2 }
    fn output_pin_count(&self) -> usize { 1 }

    fn update(&mut self, inputs: &[LogicLevel]) {
        assert_eq!(inputs.len(), self.input_pin_count(), "Incorrect number of inputs for NAND gate");
         self.output = crate::logic_level::nand(inputs[0], inputs[1]);
    }

    fn get_output(&self, pin_index: usize) -> LogicLevel {
        assert_eq!(pin_index, 0, "Invalid output pin index for NAND gate");
        self.output
    }

    fn box_clone(&self) -> Box<dyn Gate> {
        Box::new(*self)
    }
}