1pub mod dssp;
2pub mod geom;
3pub mod pdb;
4
5use std::io::{BufReader, Read, Write};
6use std::path::Path;
7
8use rsomics_common::{Result, RsomicsError};
9
10pub use dssp::assign;
11pub use pdb::Residue;
12
13#[derive(Clone, Debug)]
15pub struct Assignment {
16 pub chain: char,
17 pub seq: i32,
18 pub icode: char,
19 pub aa: char,
20 pub ss: char,
21}
22
23#[allow(clippy::missing_errors_doc)]
26pub fn assign_from_path(input: &Path) -> Result<Vec<Assignment>> {
27 let residues = if input.as_os_str() == "-" {
28 pdb::parse_pdb(BufReader::new(std::io::stdin().lock()))?
29 } else {
30 let f = std::fs::File::open(input)
31 .map_err(|e| RsomicsError::InvalidInput(format!("{}: {e}", input.display())))?;
32 pdb::parse_pdb(BufReader::new(f))?
33 };
34 if residues.is_empty() {
35 return Err(RsomicsError::InvalidInput(
36 "no protein ATOM records found".into(),
37 ));
38 }
39 let ss = assign(&residues);
40 Ok(residues
41 .iter()
42 .zip(ss)
43 .map(|(r, ss)| Assignment {
44 chain: r.chain,
45 seq: r.seq,
46 icode: r.icode,
47 aa: r.aa,
48 ss,
49 })
50 .collect())
51}
52
53#[allow(clippy::missing_errors_doc)]
56pub fn assign_from_reader<R: Read>(reader: R) -> Result<Vec<Assignment>> {
57 let residues = pdb::parse_pdb(BufReader::new(reader))?;
58 let ss = assign(&residues);
59 Ok(residues
60 .iter()
61 .zip(ss)
62 .map(|(r, ss)| Assignment {
63 chain: r.chain,
64 seq: r.seq,
65 icode: r.icode,
66 aa: r.aa,
67 ss,
68 })
69 .collect())
70}
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum Format {
75 Table,
77 Fasta,
79 String,
81}
82
83#[allow(clippy::missing_errors_doc)]
84pub fn write_output(assignments: &[Assignment], format: Format, out: &mut dyn Write) -> Result<()> {
85 match format {
86 Format::Table => {
87 for a in assignments {
88 let icode = if a.icode == ' ' { ' ' } else { a.icode };
89 writeln!(
90 out,
91 "{}\t{}{}\t{}\t{}",
92 a.chain,
93 a.seq,
94 if icode == ' ' {
95 String::new()
96 } else {
97 icode.to_string()
98 },
99 a.aa,
100 a.ss
101 )
102 .map_err(RsomicsError::Io)?;
103 }
104 }
105 Format::Fasta => {
106 let mut cur: Option<char> = None;
107 let mut buf = String::new();
108 for a in assignments {
109 if cur != Some(a.chain) {
110 if cur.is_some() {
111 writeln!(out, "{buf}").map_err(RsomicsError::Io)?;
112 }
113 writeln!(out, "> chain {}", a.chain).map_err(RsomicsError::Io)?;
114 buf.clear();
115 cur = Some(a.chain);
116 }
117 buf.push(a.ss);
118 }
119 if cur.is_some() {
120 writeln!(out, "{buf}").map_err(RsomicsError::Io)?;
121 }
122 }
123 Format::String => {
124 let s: String = assignments.iter().map(|a| a.ss).collect();
125 writeln!(out, "{s}").map_err(RsomicsError::Io)?;
126 }
127 }
128 Ok(())
129}