From 49f778f784c38a9ec40d7be1c9180cbf22a21300 Mon Sep 17 00:00:00 2001 From: Helge Eichhorn Date: Thu, 9 Nov 2023 16:47:55 +0100 Subject: [PATCH] Move time related code into separate crate --- .idea/lox-space.iml | 1 + Cargo.lock | 13 ++++ Cargo.toml | 5 ++ crates/lox-space/Cargo.toml | 1 + crates/lox-space/src/prelude.rs | 4 +- crates/lox-time/Cargo.toml | 15 ++++ .../src/time => lox-time/src}/constants.rs | 0 .../time => lox-time/src}/constants/f64.rs | 0 .../time => lox-time/src}/constants/i64.rs | 0 .../src/time => lox-time/src}/dates.rs | 68 +++++++++++++++---- .../src/time => lox-time/src}/epochs.rs | 6 +- crates/lox-time/src/errors.rs | 21 ++++++ .../src/time.rs => lox-time/src/lib.rs} | 1 + crates/lox_core/Cargo.toml | 3 +- crates/lox_core/src/bodies.rs | 2 +- crates/lox_core/src/errors.rs | 11 +-- crates/lox_core/src/lib.rs | 1 - crates/lox_core/src/two_body.rs | 6 +- crates/lox_core/tests/dates.rs | 53 --------------- crates/lox_py/Cargo.toml | 1 + crates/lox_py/src/lib.rs | 10 ++- 21 files changed, 135 insertions(+), 87 deletions(-) create mode 100644 crates/lox-time/Cargo.toml rename crates/{lox_core/src/time => lox-time/src}/constants.rs (100%) rename crates/{lox_core/src/time => lox-time/src}/constants/f64.rs (100%) rename crates/{lox_core/src/time => lox-time/src}/constants/i64.rs (100%) rename crates/{lox_core/src/time => lox-time/src}/dates.rs (83%) rename crates/{lox_core/src/time => lox-time/src}/epochs.rs (94%) create mode 100644 crates/lox-time/src/errors.rs rename crates/{lox_core/src/time.rs => lox-time/src/lib.rs} (95%) delete mode 100644 crates/lox_core/tests/dates.rs diff --git a/.idea/lox-space.iml b/.idea/lox-space.iml index 5407fa9f..2ddf744f 100644 --- a/.idea/lox-space.iml +++ b/.idea/lox-space.iml @@ -13,6 +13,7 @@ + diff --git a/Cargo.lock b/Cargo.lock index 0f62b14d..a364f4df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -328,9 +328,20 @@ dependencies = [ name = "lox-space" version = "0.0.0" dependencies = [ + "lox-time", "lox_core", ] +[[package]] +name = "lox-time" +version = "0.1.0" +dependencies = [ + "num", + "proptest", + "rstest", + "thiserror", +] + [[package]] name = "lox_core" version = "0.1.0" @@ -338,6 +349,7 @@ dependencies = [ "divan", "float_eq", "glam", + "lox-time", "num", "proptest", "rstest", @@ -355,6 +367,7 @@ dependencies = [ name = "lox_py" version = "0.1.0" dependencies = [ + "lox-time", "lox_core", "pyo3", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index e73037a1..1d5e2375 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,13 @@ license = "MPL-2.0" authors = ["Helge Eichhorn and the lox-space contributors"] [workspace.dependencies] +divan = "0.1.2" +lox-time = { path = "./crates/lox-time" } lox_core = { path = "./crates/lox_core" } lox_io = { path = "./crates/lox_io" } lox_py = { path = "./crates/lox_py" } +num = "0.4.0" +proptest = "1.1.0" +rstest = "0.18.2" thiserror = "1.0" diff --git a/crates/lox-space/Cargo.toml b/crates/lox-space/Cargo.toml index 9bd62682..efc202c2 100644 --- a/crates/lox-space/Cargo.toml +++ b/crates/lox-space/Cargo.toml @@ -9,3 +9,4 @@ rust-version.workspace = true [dependencies] lox_core.workspace = true +lox-time.workspace = true diff --git a/crates/lox-space/src/prelude.rs b/crates/lox-space/src/prelude.rs index c8ddf9d8..ec45cdee 100644 --- a/crates/lox-space/src/prelude.rs +++ b/crates/lox-space/src/prelude.rs @@ -10,7 +10,7 @@ pub use lox_core::bodies::barycenters::*; pub use lox_core::bodies::planets::*; pub use lox_core::bodies::{Ellipsoid, PointMass, Spheroid, TriAxial}; -pub use lox_core::time::dates::*; -pub use lox_core::time::epochs::*; +pub use lox_time::dates::*; +pub use lox_time::epochs::*; pub use lox_core::two_body::{Cartesian, DVec3, Keplerian, TwoBody}; diff --git a/crates/lox-time/Cargo.toml b/crates/lox-time/Cargo.toml new file mode 100644 index 00000000..2e9e23f4 --- /dev/null +++ b/crates/lox-time/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "lox-time" +version = "0.1.0" +rust-version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +num.workspace = true +thiserror.workspace = true + +[dev-dependencies] +proptest.workspace = true +rstest.workspace = true diff --git a/crates/lox_core/src/time/constants.rs b/crates/lox-time/src/constants.rs similarity index 100% rename from crates/lox_core/src/time/constants.rs rename to crates/lox-time/src/constants.rs diff --git a/crates/lox_core/src/time/constants/f64.rs b/crates/lox-time/src/constants/f64.rs similarity index 100% rename from crates/lox_core/src/time/constants/f64.rs rename to crates/lox-time/src/constants/f64.rs diff --git a/crates/lox_core/src/time/constants/i64.rs b/crates/lox-time/src/constants/i64.rs similarity index 100% rename from crates/lox_core/src/time/constants/i64.rs rename to crates/lox-time/src/constants/i64.rs diff --git a/crates/lox_core/src/time/dates.rs b/crates/lox-time/src/dates.rs similarity index 83% rename from crates/lox_core/src/time/dates.rs rename to crates/lox-time/src/dates.rs index 88a790fe..b5fd05de 100644 --- a/crates/lox_core/src/time/dates.rs +++ b/crates/lox-time/src/dates.rs @@ -3,10 +3,10 @@ * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * file, you can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::errors::LoxError; +use crate::errors::LoxTimeError; use num::ToPrimitive; #[derive(Debug, Copy, Clone)] @@ -44,15 +44,15 @@ impl Date { self.day } - pub fn new(year: i64, month: i64, day: i64) -> Result { + pub fn new(year: i64, month: i64, day: i64) -> Result { if !(1..=12).contains(&month) { - Err(LoxError::InvalidDate(year, month, day)) + Err(LoxTimeError::InvalidDate(year, month, day)) } else { let calendar = get_calendar(year, month, day); let check = Date::from_days(j2000(calendar, year, month, day))?; if check.year() != year || check.month() != month || check.day() != day { - Err(LoxError::InvalidDate(year, month, day)) + Err(LoxTimeError::InvalidDate(year, month, day)) } else { Ok(Date { calendar, @@ -64,7 +64,7 @@ impl Date { } } - pub fn from_days(offset: i64) -> Result { + pub fn from_days(offset: i64) -> Result { let calendar = if offset < LAST_JULIAN_DAY_J2K { if offset > LAST_PROLEPTIC_JULIAN_DAY_J2K { Calendar::Julian @@ -108,9 +108,9 @@ pub struct Time { } impl Time { - pub fn new(hour: i64, minute: i64, second: i64) -> Result { + pub fn new(hour: i64, minute: i64, second: i64) -> Result { if !(0..24).contains(&hour) || !(0..60).contains(&minute) || !(0..61).contains(&second) { - Err(LoxError::InvalidTime(hour, minute, second)) + Err(LoxTimeError::InvalidTime(hour, minute, second)) } else { Ok(Self { hour, @@ -151,9 +151,9 @@ impl Time { self } - pub fn from_seconds(hour: i64, minute: i64, seconds: f64) -> Result { + pub fn from_seconds(hour: i64, minute: i64, seconds: f64) -> Result { if !(0.0..61.0).contains(&seconds) { - return Err(LoxError::InvalidSeconds(hour, minute, seconds)); + return Err(LoxTimeError::InvalidSeconds(hour, minute, seconds)); } let sub = split_seconds(seconds.fract()).unwrap(); let second = seconds.round().to_i64().unwrap(); @@ -253,9 +253,9 @@ fn find_month(day_in_year: i64, is_leap: bool) -> i64 { } } -fn find_day(day_in_year: i64, month: i64, is_leap: bool) -> Result { +fn find_day(day_in_year: i64, month: i64, is_leap: bool) -> Result { if !is_leap && day_in_year > 365 { - Err(LoxError::NonLeapYear) + Err(LoxTimeError::NonLeapYear) } else { let previous_days = if is_leap { PREVIOUS_MONTH_END_DAY_LEAP @@ -314,6 +314,7 @@ fn split_seconds(seconds: f64) -> Option<[i64; 6]> { mod tests { use super::*; use proptest::prelude::*; + use rstest::rstest; proptest! { #[test] @@ -380,4 +381,47 @@ mod tests { assert!(split_seconds(2.0).is_none()); assert!(split_seconds(-0.2).is_none()); } + + #[rstest] + #[case(-4713, 12, 31, -2451546)] + #[case(-4712, 1, 1, -2451545)] + #[case(0, 12, 31, -730122)] + #[case(1, 1, 1, -730121)] + #[case(1500, 2, 28, -182554)] + #[case(1500, 2, 29, -182553)] + #[case(1500, 3, 1, -182552)] + #[case(1582, 10, 4, -152385)] + #[case(1582, 10, 15, -152384)] + #[case(1600, 2, 28, -146039)] + #[case(1600, 2, 29, -146038)] + #[case(1600, 3, 1, -146037)] + #[case(1700, 2, 28, -109514)] + #[case(1700, 3, 1, -109513)] + #[case(1800, 2, 28, -72990)] + #[case(1800, 3, 1, -72989)] + #[case(1858, 11, 15, -51546)] + #[case(1858, 11, 16, -51545)] + #[case(1999, 12, 31, -1)] + #[case(2000, 1, 1, 0)] + #[case(2000, 2, 28, 58)] + #[case(2000, 2, 29, 59)] + #[case(2000, 3, 1, 60)] + fn test_dates(#[case] year: i64, #[case] month: i64, #[case] day: i64, #[case] exp: i64) { + let date = Date::new(year, month, day).expect("date should be valid"); + assert_eq!(exp, date.j2000()) + } + + #[test] + fn test_illegal_dates() { + assert!(Date::new(2018, 2, 29).is_err()); + assert!(Date::new(2018, 0, 1).is_err()); + assert!(Date::new(2018, 13, 1).is_err()); + } + + #[test] + fn test_illegal_times() { + assert!(Time::from_seconds(24, 59, 59.0).is_err()); + assert!(Time::from_seconds(23, 60, 59.0).is_err()); + assert!(Time::from_seconds(23, 59, 61.0).is_err()); + } } diff --git a/crates/lox_core/src/time/epochs.rs b/crates/lox-time/src/epochs.rs similarity index 94% rename from crates/lox_core/src/time/epochs.rs rename to crates/lox-time/src/epochs.rs index 342ae24c..eb3a14f5 100644 --- a/crates/lox_core/src/time/epochs.rs +++ b/crates/lox-time/src/epochs.rs @@ -9,11 +9,11 @@ use std::fmt; use std::fmt::Formatter; -use crate::time::constants; -use crate::time::constants::i64::{SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE}; use num::ToPrimitive; -use crate::time::dates::{Date, DateTime, Time}; +use crate::constants; +use crate::constants::i64::{SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE}; +use crate::dates::{Date, DateTime, Time}; #[derive(Debug, Copy, Clone)] pub enum TimeScale { diff --git a/crates/lox-time/src/errors.rs b/crates/lox-time/src/errors.rs new file mode 100644 index 00000000..a746731b --- /dev/null +++ b/crates/lox-time/src/errors.rs @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023. Helge Eichhorn and the LOX contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at http://mozilla.org/MPL/2.0/. + */ + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum LoxTimeError { + #[error("invalid date `{0}-{1}-{2}`")] + InvalidDate(i64, i64, i64), + #[error("invalid time `{0}:{1}:{2}`")] + InvalidTime(i64, i64, i64), + #[error("invalid time `{0}:{1}:{2}`")] + InvalidSeconds(i64, i64, f64), + #[error("day of year cannot be 366 for a non-leap year")] + NonLeapYear, +} diff --git a/crates/lox_core/src/time.rs b/crates/lox-time/src/lib.rs similarity index 95% rename from crates/lox_core/src/time.rs rename to crates/lox-time/src/lib.rs index 954a7ffc..1aa2a301 100644 --- a/crates/lox_core/src/time.rs +++ b/crates/lox-time/src/lib.rs @@ -9,3 +9,4 @@ pub mod constants; pub mod dates; pub mod epochs; +pub mod errors; diff --git a/crates/lox_core/Cargo.toml b/crates/lox_core/Cargo.toml index b90e4a25..da99cec6 100644 --- a/crates/lox_core/Cargo.toml +++ b/crates/lox_core/Cargo.toml @@ -6,8 +6,9 @@ edition = "2021" [dependencies] float_eq = "1.0.1" glam = "0.24.2" -num = "0.4.0" +num.workspace = true thiserror.workspace = true +lox-time.workspace = true [dev-dependencies] proptest = "1.1.0" diff --git a/crates/lox_core/src/bodies.rs b/crates/lox_core/src/bodies.rs index c83d2b26..e347cf21 100644 --- a/crates/lox_core/src/bodies.rs +++ b/crates/lox_core/src/bodies.rs @@ -8,7 +8,7 @@ use std::f64::consts::PI; -use crate::time::constants::f64::{SECONDS_PER_DAY, SECONDS_PER_JULIAN_CENTURY}; +use lox_time::constants::f64::{SECONDS_PER_DAY, SECONDS_PER_JULIAN_CENTURY}; pub mod barycenters; pub mod minor; diff --git a/crates/lox_core/src/errors.rs b/crates/lox_core/src/errors.rs index 7ce753fb..f4bc9fec 100644 --- a/crates/lox_core/src/errors.rs +++ b/crates/lox_core/src/errors.rs @@ -6,16 +6,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use lox_time::errors::LoxTimeError; use thiserror::Error; #[derive(Error, Debug)] pub enum LoxError { - #[error("invalid date `{0}-{1}-{2}`")] - InvalidDate(i64, i64, i64), - #[error("invalid time `{0}:{1}:{2}`")] - InvalidTime(i64, i64, i64), - #[error("invalid time `{0}:{1}:{2}`")] - InvalidSeconds(i64, i64, f64), - #[error("day of year cannot be 366 for a non-leap year")] - NonLeapYear, + #[error(transparent)] + LoxTimeError(#[from] LoxTimeError), } diff --git a/crates/lox_core/src/lib.rs b/crates/lox_core/src/lib.rs index 97efaf5a..7cfdd0df 100644 --- a/crates/lox_core/src/lib.rs +++ b/crates/lox_core/src/lib.rs @@ -10,5 +10,4 @@ pub mod bodies; pub mod communications; pub mod errors; pub mod frames; -pub mod time; pub mod two_body; diff --git a/crates/lox_core/src/two_body.rs b/crates/lox_core/src/two_body.rs index 0f926933..7f7586f6 100644 --- a/crates/lox_core/src/two_body.rs +++ b/crates/lox_core/src/two_body.rs @@ -9,8 +9,8 @@ pub use glam::DVec3; use crate::bodies::{gravitational_parameter, PointMass}; -use crate::time::epochs::Epoch; use crate::two_body::elements::{cartesian_to_keplerian, keplerian_to_cartesian}; +use lox_time::epochs::Epoch; pub mod elements; @@ -225,8 +225,8 @@ mod tests { use std::ops::Mul; use crate::bodies::planets::Earth; - use crate::time::dates::{Date, Time}; - use crate::time::epochs::TimeScale; + use lox_time::dates::{Date, Time}; + use lox_time::epochs::TimeScale; use super::*; diff --git a/crates/lox_core/tests/dates.rs b/crates/lox_core/tests/dates.rs deleted file mode 100644 index 6c1e2a6a..00000000 --- a/crates/lox_core/tests/dates.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2023. Helge Eichhorn and the LOX contributors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -use lox_core::time::dates::{Date, Time}; -use rstest::rstest; - -#[rstest] -#[case(-4713, 12, 31, -2451546)] -#[case(-4712, 1, 1, -2451545)] -#[case(0, 12, 31, -730122)] -#[case(1, 1, 1, -730121)] -#[case(1500, 2, 28, -182554)] -#[case(1500, 2, 29, -182553)] -#[case(1500, 3, 1, -182552)] -#[case(1582, 10, 4, -152385)] -#[case(1582, 10, 15, -152384)] -#[case(1600, 2, 28, -146039)] -#[case(1600, 2, 29, -146038)] -#[case(1600, 3, 1, -146037)] -#[case(1700, 2, 28, -109514)] -#[case(1700, 3, 1, -109513)] -#[case(1800, 2, 28, -72990)] -#[case(1800, 3, 1, -72989)] -#[case(1858, 11, 15, -51546)] -#[case(1858, 11, 16, -51545)] -#[case(1999, 12, 31, -1)] -#[case(2000, 1, 1, 0)] -#[case(2000, 2, 28, 58)] -#[case(2000, 2, 29, 59)] -#[case(2000, 3, 1, 60)] -fn test_dates(#[case] year: i64, #[case] month: i64, #[case] day: i64, #[case] exp: i64) { - let date = Date::new(year, month, day).expect("date should be valid"); - assert_eq!(exp, date.j2000()) -} - -#[test] -fn test_illegal_dates() { - assert!(Date::new(2018, 2, 29).is_err()); - assert!(Date::new(2018, 0, 1).is_err()); - assert!(Date::new(2018, 13, 1).is_err()); -} - -#[test] -fn test_illegal_times() { - assert!(Time::from_seconds(24, 59, 59.0).is_err()); - assert!(Time::from_seconds(23, 60, 59.0).is_err()); - assert!(Time::from_seconds(23, 59, 61.0).is_err()); -} diff --git a/crates/lox_py/Cargo.toml b/crates/lox_py/Cargo.toml index 87d8ab13..7716f535 100644 --- a/crates/lox_py/Cargo.toml +++ b/crates/lox_py/Cargo.toml @@ -14,4 +14,5 @@ crate-type = ["cdylib"] [dependencies] pyo3 = "0.20.0" lox_core.workspace = true +lox-time.workspace = true thiserror.workspace = true diff --git a/crates/lox_py/src/lib.rs b/crates/lox_py/src/lib.rs index 538dec79..d5442aa9 100644 --- a/crates/lox_py/src/lib.rs +++ b/crates/lox_py/src/lib.rs @@ -11,9 +11,10 @@ use pyo3::prelude::*; use thiserror::Error; use lox_core::errors::LoxError; -use lox_core::time::dates::{Date, Time}; -use lox_core::time::epochs::Epoch; -use lox_core::time::epochs::TimeScale; +use lox_time::dates::{Date, Time}; +use lox_time::epochs::Epoch; +use lox_time::epochs::TimeScale; +use lox_time::errors::LoxTimeError; #[derive(Error, Debug)] pub enum LoxPyError { @@ -22,6 +23,8 @@ pub enum LoxPyError { #[error(transparent)] LoxError(#[from] LoxError), #[error(transparent)] + LoxTimeError(#[from] LoxTimeError), + #[error(transparent)] PyError(#[from] PyErr), } @@ -30,6 +33,7 @@ impl From for PyErr { match value { LoxPyError::InvalidTimeScale(_) => PyValueError::new_err(value.to_string()), LoxPyError::LoxError(value) => PyValueError::new_err(value.to_string()), + LoxPyError::LoxTimeError(value) => PyValueError::new_err(value.to_string()), LoxPyError::PyError(value) => value, } }