From c5a0ae57938d2fe5621063dd255fe076e91130f1 Mon Sep 17 00:00:00 2001 From: "Kevin R. Thornton" Date: Mon, 18 Sep 2023 10:52:00 -0700 Subject: [PATCH] refactor: Use generics to improve builder ergonomics * improve doc tests * migration builders now use builder pattern to allow ergonomic use of generics --- demes/src/builder.rs | 172 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 140 insertions(+), 32 deletions(-) diff --git a/demes/src/builder.rs b/demes/src/builder.rs index d34d4a43..032cec0c 100644 --- a/demes/src/builder.rs +++ b/demes/src/builder.rs @@ -44,9 +44,9 @@ pub struct GraphBuilder { /// /// See [`GraphBuilder::add_migration`]. #[derive(Clone, Default)] -pub struct SymmetricMigrationBuilder, S: std::ops::Deref> { +pub struct SymmetricMigrationBuilder { /// The demes that are involved - pub demes: Option, + pub demes: Option>, /// The start time pub start_time: Option, /// The end time @@ -55,17 +55,57 @@ pub struct SymmetricMigrationBuilder, S: std::ops::Deref, } -impl, D: std::ops::Deref> From> - for UnresolvedMigration -{ - fn from(value: SymmetricMigrationBuilder) -> Self { - let demes = value.demes.map(|v| { - v.iter() - .map(|a| a.as_ref().to_owned()) - .collect::>() - }); +impl SymmetricMigrationBuilder { + /// Set the demes + pub fn set_demes(self, d: D) -> Self + where + D: std::ops::Deref, + A: AsRef, + { + Self { + demes: Some(d.iter().map(|a| a.as_ref().to_owned()).collect::>()), + ..self + } + } + + /// Set the start time + pub fn set_start_time(self, time: T) -> Self + where + T: Into, + { + Self { + start_time: Some(time.into()), + ..self + } + } + + /// Set the end time + pub fn set_end_time(self, time: T) -> Self + where + T: Into, + { + Self { + end_time: Some(time.into()), + ..self + } + } + + /// Set the symmetric migration rate among all `demes`. + pub fn set_rate(self, rate: R) -> Self + where + R: Into, + { + Self { + rate: Some(rate.into()), + ..self + } + } +} + +impl From for UnresolvedMigration { + fn from(value: SymmetricMigrationBuilder) -> Self { Self { - demes, + demes: value.demes, start_time: value.start_time, end_time: value.end_time, rate: value.rate, @@ -76,11 +116,11 @@ impl, D: std::ops::Deref> From> { +pub struct AsymmetricMigrationBuilder { /// The source deme - pub source: Option, + pub source: Option, /// The destination deme - pub dest: Option, + pub dest: Option, /// The start time pub start_time: Option, /// The end time @@ -89,13 +129,68 @@ pub struct AsymmetricMigrationBuilder> { pub rate: Option, } -impl> From> for UnresolvedMigration { - fn from(value: AsymmetricMigrationBuilder) -> Self { - let source = value.source.map(|s| s.as_ref().to_owned()); - let dest = value.dest.map(|s| s.as_ref().to_owned()); +impl AsymmetricMigrationBuilder { + /// Set the source deme + pub fn set_source(self, source: A) -> Self + where + A: AsRef, + { + Self { + source: Some(source.as_ref().to_owned()), + ..self + } + } + + /// Set the destination deme + pub fn set_dest(self, dest: A) -> Self + where + A: AsRef, + { + Self { + dest: Some(dest.as_ref().to_owned()), + ..self + } + } + + /// Set the start time + pub fn set_start_time(self, time: T) -> Self + where + T: Into, + { + Self { + start_time: Some(time.into()), + ..self + } + } + + /// Set the end time + pub fn set_end_time(self, time: T) -> Self + where + T: Into, + { + Self { + end_time: Some(time.into()), + ..self + } + } + + /// Set the rate from `source` to `test` + pub fn set_rate(self, rate: R) -> Self + where + R: Into, + { + Self { + rate: Some(rate.into()), + ..self + } + } +} + +impl From for UnresolvedMigration { + fn from(value: AsymmetricMigrationBuilder) -> Self { Self { - source, - dest, + source: value.source, + dest: value.dest, rate: value.rate, start_time: value.start_time, end_time: value.end_time, @@ -146,14 +241,14 @@ impl GraphBuilder { /// ``` /// /// # Notes - pub fn add_deme( + pub fn add_deme>( &mut self, name: &str, - epochs: Vec, + epochs: D, history: UnresolvedDemeHistory, description: Option<&str>, ) { - let ptr = UnresolvedDeme::new_via_builder(name, epochs, history, description); + let ptr = UnresolvedDeme::new_via_builder(name, epochs.to_owned(), history, description); self.graph.add_deme(ptr); } @@ -172,7 +267,8 @@ impl GraphBuilder { /// let mut b = demes::GraphBuilder::new_generations(None); /// b.add_deme("A", vec![epoch], history.clone(), Some("this is deme A")); /// b.add_deme("B", vec![epoch], history, Some("this is deme B")); - /// b.add_migration(demes::AsymmetricMigrationBuilder{source: Some("A"), dest: Some("B"), rate: Some(1e-4.into()), ..Default::default()}); + /// let migration = demes::AsymmetricMigrationBuilder::default().set_source("A").set_dest("B").set_rate(1e-4); + /// b.add_migration(migration); /// b.resolve().unwrap(); /// ``` /// @@ -187,7 +283,8 @@ impl GraphBuilder { /// let mut b = demes::GraphBuilder::new_generations(None); /// b.add_deme("A", vec![epoch], history.clone(), Some("this is deme A")); /// b.add_deme("B", vec![epoch], history, Some("this is deme B")); - /// b.add_migration(demes::SymmetricMigrationBuilder{demes: Some(["A", "B"].as_slice()), rate: Some(1e-4.into()), ..Default::default()}); + /// let migration = demes::SymmetricMigrationBuilder::default().set_demes(["A","B"].as_slice()).set_rate(1e-4); + /// b.add_migration(migration); /// b.resolve().unwrap(); /// ``` /// @@ -200,7 +297,8 @@ impl GraphBuilder { /// # let mut b = demes::GraphBuilder::new_generations(None); /// # b.add_deme("A", vec![epoch], history.clone(), Some("this is deme A")); /// # b.add_deme("B", vec![epoch], history, Some("this is deme B")); - /// b.add_migration(demes::SymmetricMigrationBuilder{demes: Some(vec!["A", "B"]), rate: Some(1e-4.into()), ..Default::default()}); + /// let migration = demes::SymmetricMigrationBuilder::default().set_demes(vec!["A","B"]).set_rate(1e-4); + /// b.add_migration(migration); /// # b.resolve().unwrap(); /// ``` /// @@ -236,19 +334,29 @@ impl GraphBuilder { /// b.add_deme("B", vec![epoch], history, Some("this is deme B")); /// b.add_pulse(Some(&["A"]), /// Some("B"), - /// Some(demes::InputTime::from(50.0)), - /// Some(vec![demes::InputProportion::from(0.5)])); + /// Some(50.0), + /// Some([0.5].as_slice())); /// b.resolve().unwrap(); /// ``` - pub fn add_pulse( + pub fn add_pulse< + T: Into, + P: Into + Copy, + D: std::ops::Deref, + >( &mut self, sources: Option<&[&str]>, dest: Option<&str>, - time: Option, - proportions: Option>, + time: Option, + proportions: Option, ) { let sources = sources.map(|value| value.iter().map(|v| v.to_string()).collect::>()); let dest = dest.map(|value| value.to_string()); + let time = time.map(|t| t.into()); + let proportions = proportions.map(|s| { + s.iter() + .map(|p| (*p).into()) + .collect::>() + }); self.graph.add_pulse(sources, dest, time, proportions); }