primer3 0.1.0

Safe Rust bindings to the primer3 primer design library
Documentation
//! Sequence library for mispriming and mishybridization checks.
//!
//! Primer3 can check candidate primers against a library of sequences
//! (e.g., human repeat elements) to avoid off-target binding.

/// An entry in a [`SequenceLibrary`], containing a name, sequence, and weight.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SequenceEntry {
    /// The name of this sequence (e.g., "ALU").
    pub name: String,
    /// The DNA sequence.
    pub sequence: String,
    /// The weight for this sequence (default: 1.0).
    /// Higher weights increase the penalty for primer similarity to this sequence.
    pub weight: f64,
}

/// A library of sequences for mispriming or mishybridization checks.
///
/// This is a collection of named, weighted sequences (like a FASTA file in memory).
/// Primer3 will check candidate primers against these sequences to penalize
/// or reject primers with high similarity.
///
/// Note: Reverse complements are computed internally when passed to the C library;
/// you only need to provide the forward strand.
///
/// # Example
///
/// ```
/// use primer3::SequenceLibrary;
///
/// let mut lib = SequenceLibrary::new();
/// lib.add("ALU", "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCA...");
/// lib.add("LINE1", "ATGGCTTCAACCTGGGAGGCG...");
/// lib.add_weighted("HIGH_PRIORITY", "ATCGATCG...", 2.0);
/// ```
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SequenceLibrary {
    entries: Vec<SequenceEntry>,
}

impl SequenceLibrary {
    /// Creates an empty sequence library.
    pub fn new() -> Self {
        Self::default()
    }

    /// Adds a named sequence to the library with the default weight of 1.0.
    pub fn add(&mut self, name: impl Into<String>, sequence: impl Into<String>) {
        self.entries.push(SequenceEntry {
            name: name.into(),
            sequence: sequence.into(),
            weight: 1.0,
        });
    }

    /// Adds a named sequence to the library with a custom weight.
    pub fn add_weighted(
        &mut self,
        name: impl Into<String>,
        sequence: impl Into<String>,
        weight: f64,
    ) {
        self.entries.push(SequenceEntry { name: name.into(), sequence: sequence.into(), weight });
    }

    /// Returns the number of sequences in the library.
    pub fn len(&self) -> usize {
        self.entries.len()
    }

    /// Returns true if the library contains no sequences.
    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }

    /// Iterates over the sequence entries.
    pub fn iter(&self) -> impl Iterator<Item = &SequenceEntry> {
        self.entries.iter()
    }
}

impl FromIterator<(String, String)> for SequenceLibrary {
    fn from_iter<I: IntoIterator<Item = (String, String)>>(iter: I) -> Self {
        Self {
            entries: iter
                .into_iter()
                .map(|(name, sequence)| SequenceEntry { name, sequence, weight: 1.0 })
                .collect(),
        }
    }
}

impl FromIterator<(String, String, f64)> for SequenceLibrary {
    fn from_iter<I: IntoIterator<Item = (String, String, f64)>>(iter: I) -> Self {
        Self {
            entries: iter
                .into_iter()
                .map(|(name, sequence, weight)| SequenceEntry { name, sequence, weight })
                .collect(),
        }
    }
}