From 4f4bb3f369eeba29e5601c22a094005dd787ddba Mon Sep 17 00:00:00 2001
From: Martin Larralde <martin.larralde@embl.de>
Date: Mon, 17 Jun 2024 18:06:47 +0200
Subject: [PATCH] Improve documentation of `lightmotif_io::transfac`

---
 lightmotif-io/src/error.rs           | 23 +++++++++++
 lightmotif-io/src/transfac/mod.rs    | 57 +++++++++++++++++++++++++++-
 lightmotif-io/src/transfac/reader.rs |  2 +
 3 files changed, 80 insertions(+), 2 deletions(-)

diff --git a/lightmotif-io/src/error.rs b/lightmotif-io/src/error.rs
index 4cb9707..bfd5a2f 100644
--- a/lightmotif-io/src/error.rs
+++ b/lightmotif-io/src/error.rs
@@ -1,3 +1,6 @@
+use std::fmt::Display;
+use std::fmt::Formatter;
+
 use nom::error::Error as NomError;
 
 #[derive(Debug)]
@@ -34,3 +37,23 @@ impl From<nom::Err<NomError<&'_ str>>> for Error {
         }
     }
 }
+
+impl Display for Error {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Error::InvalidData => f.write_str("invalid data"),
+            Error::Io(err) => err.fmt(f),
+            Error::Nom(err) => err.fmt(f),
+        }
+    }
+}
+
+impl std::error::Error for Error {
+    fn cause(&self) -> Option<&dyn std::error::Error> {
+        match self {
+            Error::InvalidData => None,
+            Error::Io(e) => Some(e),
+            Error::Nom(e) => Some(e),
+        }
+    }
+}
diff --git a/lightmotif-io/src/transfac/mod.rs b/lightmotif-io/src/transfac/mod.rs
index 42b7ef7..777d494 100644
--- a/lightmotif-io/src/transfac/mod.rs
+++ b/lightmotif-io/src/transfac/mod.rs
@@ -1,3 +1,5 @@
+//! Parser implementation for the TRANSFAC format.
+
 use std::io::BufRead;
 
 use lightmotif::abc::Alphabet;
@@ -12,6 +14,7 @@ mod reader;
 
 pub use self::reader::Reader;
 
+/// A TRANSFAC record.
 #[derive(Debug, Clone)]
 pub struct Record<A: Alphabet> {
     id: Option<String>,
@@ -50,7 +53,12 @@ impl<A: Alphabet> Record<A> {
         self.data.as_ref()
     }
 
-    /// The raw data found in the matrix.
+    /// The references associated with the record.
+    pub fn references(&self) -> &[Reference] {
+        &self.references
+    }
+
+    /// Get the record matrix as an integer count data.
     pub fn to_counts(&self) -> Option<CountMatrix<A>> {
         if let Some(data) = &self.data {
             let mut counts = DenseMatrix::<u32, A::K>::new(data.rows());
@@ -69,6 +77,7 @@ impl<A: Alphabet> Record<A> {
         }
     }
 
+    /// Get the record matrix as a frequency matrix.
     pub fn to_freq<P>(&self, pseudo: P) -> Option<FrequencyMatrix<A>>
     where
         P: Into<Pseudocounts<A>>,
@@ -116,10 +125,12 @@ pub struct ReferenceNumber {
 }
 
 impl ReferenceNumber {
+    /// Create a new reference number with the given number.
     pub fn new(local: u32) -> Self {
-        Self { local, xref: None }
+        Self::with_xref(local, None)
     }
 
+    /// Create a new reference number with the given number and cross-reference.
     pub fn with_xref<X>(local: u32, xref: X) -> Self
     where
         X: Into<Option<String>>,
@@ -129,6 +140,16 @@ impl ReferenceNumber {
             xref: xref.into(),
         }
     }
+
+    /// The local number of the reference number.
+    pub fn local(&self) -> u32 {
+        self.local
+    }
+
+    /// The cross-reference, if any.
+    pub fn xref(&self) -> Option<&str> {
+        self.xref.as_ref().map(String::as_str)
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -140,6 +161,38 @@ pub struct Reference {
     pmid: Option<String>,
 }
 
+impl Reference {
+    /// Create a new reference with the given reference number.
+    pub fn new(number: ReferenceNumber) -> Self {
+        Self {
+            number,
+            title: None,
+            link: None,
+            pmid: None,
+        }
+    }
+
+    /// The number of the reference.
+    pub fn number(&self) -> &ReferenceNumber {
+        &self.number
+    }
+
+    /// The title of the reference, if any.
+    pub fn title(&self) -> Option<&str> {
+        self.title.as_ref().map(String::as_str)
+    }
+
+    /// A link to the reference, if any.
+    pub fn link(&self) -> Option<&str> {
+        self.link.as_ref().map(String::as_str)
+    }
+
+    /// The PubMed ID of the reference, if any.
+    pub fn pmid(&self) -> Option<&str> {
+        self.pmid.as_ref().map(String::as_str)
+    }
+}
+
 pub fn read<B: BufRead, A: Alphabet>(reader: B) -> self::reader::Reader<B, A> {
     self::reader::Reader::new(reader)
 }
diff --git a/lightmotif-io/src/transfac/reader.rs b/lightmotif-io/src/transfac/reader.rs
index 1a0b485..6a8dcbf 100644
--- a/lightmotif-io/src/transfac/reader.rs
+++ b/lightmotif-io/src/transfac/reader.rs
@@ -6,6 +6,8 @@ use lightmotif::Alphabet;
 use super::Record;
 use crate::error::Error;
 
+/// A reader for TRANSFAC-formatted files.
+#[derive(Debug)]
 pub struct Reader<B: BufRead, A: Alphabet> {
     buffer: String,
     bufread: B,
-- 
GitLab