sfs_core/input/site/reader/
builder.rs1use std::{collections::HashSet, fmt, io, path::PathBuf};
4
5use sample::Sample;
6
7use crate::{
8 array::Shape,
9 input::{genotype, sample},
10 spectrum::project::{PartialProjection, ProjectionError},
11};
12
13#[derive(Debug, Default)]
15pub struct Builder {
16 samples: Option<Option<Samples>>,
17 project: Option<Option<Project>>,
18}
19
20impl Builder {
21 pub fn build(self, reader: genotype::reader::DynReader) -> Result<super::Reader, Error> {
27 let sample_map = match self.samples.unwrap_or(None) {
28 Some(Samples::List(list)) => sample::Map::from_iter(list),
29 Some(Samples::Path(path)) => sample::Map::from_path(path)?,
30 None => sample::Map::from_all(reader.samples().iter().cloned()),
31 };
32
33 if sample_map.is_empty() {
34 return Err(Error::EmptySamplesMap);
35 }
36
37 let reader_samples = HashSet::<_>::from_iter(reader.samples());
39 if let Some(unknown_sample) = sample_map
40 .samples()
41 .find(|sample| !reader_samples.contains(sample))
42 {
43 return Err(Error::UnknownSample {
44 sample: unknown_sample.as_ref().to_string(),
45 });
46 }
47
48 let projection = if let Some(project_to) = self.project.unwrap_or(None).map(Project::shape)
49 {
50 let project_from = sample_map.shape();
51
52 if project_from.dimensions() != project_to.dimensions() {
53 return Err(ProjectionError::UnequalDimensions {
54 from: project_from.dimensions(),
55 to: project_to.dimensions(),
56 }
57 .into());
58 } else if let Some((dimension, (&from, &to))) = project_from
59 .iter()
60 .zip(project_to.iter())
61 .enumerate()
62 .find(|(_, (from, to))| from < to)
63 {
64 return Err(ProjectionError::InvalidProjection {
65 dimension,
66 from,
67 to,
68 }
69 .into());
70 } else {
71 Some(PartialProjection::from_shape(project_to)?)
72 }
73 } else {
74 None
75 };
76
77 Ok(super::Reader::new_unchecked(reader, sample_map, projection))
78 }
79
80 pub fn set_project(mut self, project: Option<Project>) -> Self {
84 self.project = Some(project);
85 self
86 }
87
88 pub fn set_samples(mut self, samples: Option<Samples>) -> Self {
92 self.samples = Some(samples);
93 self
94 }
95}
96
97#[derive(Debug)]
99pub enum Samples {
100 Path(PathBuf),
102 List(Vec<(Sample, sample::Population)>),
104}
105
106#[derive(Debug)]
108pub enum Project {
109 Individuals(Vec<usize>),
111 Shape(Shape),
113}
114
115impl Project {
116 fn shape(self) -> Shape {
117 match self {
118 Project::Individuals(individuals) => {
119 Shape(individuals.into_iter().map(|i| 2 * i + 1).collect())
120 }
121 Project::Shape(shape) => shape,
122 }
123 }
124}
125
126#[derive(Debug)]
128pub enum Error {
129 EmptySamplesMap,
131 Io(io::Error),
133 PathDoesNotExist {
135 path: PathBuf,
137 },
138 Projection(ProjectionError),
140 UnknownSample {
142 sample: String,
144 },
145}
146
147impl From<io::Error> for Error {
148 fn from(e: io::Error) -> Self {
149 Self::Io(e)
150 }
151}
152
153impl From<ProjectionError> for Error {
154 fn from(e: ProjectionError) -> Self {
155 Self::Projection(e)
156 }
157}
158
159impl fmt::Display for Error {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 match self {
162 Error::EmptySamplesMap => f.write_str("empty samples mapping"),
163 Error::Io(e) => write!(f, "{e}"),
164 Error::PathDoesNotExist { path } => {
165 write!(f, "path '{}' not found", path.display())
166 }
167 Error::UnknownSample { sample } => write!(f, "unknown sample {sample}"),
168 Error::Projection(e) => write!(f, "{e}"),
169 }
170 }
171}
172
173impl std::error::Error for Error {}