diff --git a/.gitignore b/.gitignore index 9309bfa..a4138ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Artifacts +build*/ +coverage*/ + # Prerequisites *.d diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0ac1f7d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,62 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- `CivilTime` struct for UTC date-time breakdown, replacing forward declaration in scales. + - Supports construction from individual components and conversion to/from C FFI `tempoch_utc_t`. + - Stream operator support for formatted output as YYYY-MM-DD HH:MM:SS[.nnnnnnnnn]. +- `TimeScaleTraits` specializations for multiple time scales: + - `JDScale` — Julian Date (days since −4712‑01‑01T12:00 TT) + - `MJDScale` — Modified Julian Date (JD − 2 400 000.5) + - `UTCScale` — UTC (internally represented as MJD days) + - `TTScale` — Terrestrial Time + - `TAIScale` — International Atomic Time + - `TDBScale` — Barycentric Dynamical Time + - `TCGScale` — Geocentric Coordinate Time + - `TCBScale` — Barycentric Coordinate Time + - `GPSScale` — GPS Time + - `UTScale` — Universal Time (UT1) + - `JDEScale` — Julian Ephemeris Date + - `UnixTimeScale` — Unix (POSIX) time +- Type aliases for convenience: + - `using JulianDate = Time` + - `using MJD = Time` + - `using TDB = Time` + - `using TT = Time` + - `using TAI = Time` + - `using TCG = Time` + - `using TCB = Time` + - `using GPS = Time` + - `using UT = Time` + - `using UniversalTime = Time` + - `using JDE = Time` + - `using UnixTime = Time` +- Local CI runner script (`run-ci.sh`) that executes lint, build, test, and coverage checks. +- Code coverage reporting to CI pipeline via gcovr (XML and HTML reports). +- `.gitignore` entries for build outputs and coverage artifacts. + +### Changed + +- Reorganized codebase to separate `CivilTime` definition into dedicated header (`civil_time.hpp`) to resolve circular dependencies. +- Refactored `scales.hpp` to include full `CivilTime` definition and improve template specialization organization. +- Updated `time_base.hpp` to use new `civil_time.hpp` header. + +### Fixed + +- Fixed clang-format violations in header files. +- Resolved incomplete type errors in clang-tidy by properly completing `CivilTime` definition before use in template specializations. +- Improved include chain to ensure all FFI-backed time scale conversions are properly typed and compiled. + +## [0.1.0] + +- Initial C++ wrapper for tempoch FFI library. +- Core `Time` class template with FFI-backed time scale dispatch. +- Period arithmetic and interval operations. +- Example programs demonstrating time scale conversions and period operations. diff --git a/coverage.xml b/coverage.xml new file mode 100644 index 0000000..d26eff0 --- /dev/null +++ b/coverage.xml @@ -0,0 +1,3 @@ + + +. \ No newline at end of file diff --git a/include/tempoch/civil_time.hpp b/include/tempoch/civil_time.hpp new file mode 100644 index 0000000..240a123 --- /dev/null +++ b/include/tempoch/civil_time.hpp @@ -0,0 +1,76 @@ +#pragma once + +/** + * @file civil_time.hpp + * @brief UTC date-time breakdown struct. + * + * Forward-declared in scales.hpp and fully defined here to avoid circular + * dependencies. + */ + +#include "ffi_core.hpp" +#include +#include + +namespace tempoch { + +/** + * @brief UTC date-time breakdown. + * + * A simple value type mirroring the C `tempoch_utc_t` struct. + * + * @code + * tempoch::CivilTime noon(2000, 1, 1, 12, 0, 0); + * auto jd = tempoch::JulianDate::from_utc(noon); + * @endcode + */ +struct CivilTime { + int32_t year; ///< Gregorian year (astronomical year numbering). + uint8_t month; ///< Month [1, 12]. + uint8_t day; ///< Day of month [1, 31]. + uint8_t hour; ///< Hour [0, 23]. + uint8_t minute; ///< Minute [0, 59]. + uint8_t second; ///< Second [0, 60] (leap-second aware). + uint32_t nanosecond; ///< Nanosecond [0, 999 999 999]. + + /// Default constructor: J2000 epoch noon-like civil representation. + CivilTime() + : year(2000), month(1), day(1), hour(12), minute(0), second(0), + nanosecond(0) {} + + /** + * @brief Construct from civil UTC components. + */ + CivilTime(int32_t y, uint8_t mo, uint8_t d, uint8_t h = 0, uint8_t mi = 0, + uint8_t s = 0, uint32_t ns = 0) + : year(y), month(mo), day(d), hour(h), minute(mi), second(s), + nanosecond(ns) {} + + /// Convert to the C FFI struct. + tempoch_utc_t to_c() const { + return {year, month, day, hour, minute, second, nanosecond}; + } + + /// Create from the C FFI struct. + static CivilTime from_c(const tempoch_utc_t &c) { + return CivilTime(c.year, c.month, c.day, c.hour, c.minute, c.second, + c.nanosecond); + } +}; + +/// Stream CivilTime as YYYY-MM-DD HH:MM:SS[.nnnnnnnnn]. +inline std::ostream &operator<<(std::ostream &os, const CivilTime &u) { + const char prev = os.fill(); + os << u.year << '-' << std::setfill('0') << std::setw(2) + << static_cast(u.month) << '-' << std::setw(2) + << static_cast(u.day) << ' ' << std::setw(2) + << static_cast(u.hour) << ':' << std::setw(2) + << static_cast(u.minute) << ':' << std::setw(2) + << static_cast(u.second); + if (u.nanosecond != 0) + os << '.' << std::setw(9) << u.nanosecond; + os.fill(prev); + return os; +} + +} // namespace tempoch diff --git a/include/tempoch/scales.hpp b/include/tempoch/scales.hpp index f88e0d1..845cc0e 100644 --- a/include/tempoch/scales.hpp +++ b/include/tempoch/scales.hpp @@ -13,13 +13,11 @@ * 3. Specialise `TimeConvertTraits` for each supported conversion pair. */ -#include "ffi_core.hpp" // tempoch_ffi.h + check_status +#include "civil_time.hpp" // CivilTime struct definition +#include "ffi_core.hpp" // tempoch_ffi.h + check_status namespace tempoch { -// Forward declaration — defined in time_base.hpp. -struct CivilTime; - // ============================================================================ // Scale Tags // ============================================================================ @@ -33,15 +31,32 @@ struct MJDScale {}; /// UTC, internally stored as MJD days for arithmetic. struct UTCScale {}; -// Stubs for future FFI-backed scales — uncomment and specialise traits once -// the FFI exposes the conversion functions. -// struct TTScale {}; // Terrestrial Time -// struct TAIScale {}; // International Atomic Time -// struct TDBScale {}; // Barycentric Dynamical Time -// struct TCGScale {}; // Geocentric Coordinate Time -// struct TCBScale {}; // Barycentric Coordinate Time -// struct GPSScale {}; // GPS Time -// struct UTScale {}; // Universal Time (UT1) +/// Terrestrial Time (TT), stored as JD days in TT scale. +struct TTScale {}; + +/// International Atomic Time (TAI), stored as JD days in TAI scale. +struct TAIScale {}; + +/// Barycentric Dynamical Time (TDB), stored as JD days in TDB scale. +struct TDBScale {}; + +/// Geocentric Coordinate Time (TCG), stored as JD days in TCG scale. +struct TCGScale {}; + +/// Barycentric Coordinate Time (TCB), stored as JD days in TCB scale. +struct TCBScale {}; + +/// GPS Time, stored as JD days in GPS scale. +struct GPSScale {}; + +/// Universal Time (UT1), stored as JD days in UT1 scale. +struct UTScale {}; + +/// Julian Ephemeris Date (JDE ≡ TDB), stored as JD days. +struct JDEScale {}; + +/// Unix Time (seconds since 1970-01-01T00:00:00 UTC), stored as Unix seconds. +struct UnixTimeScale {}; // ============================================================================ // TimeScaleTraits — per-scale FFI dispatch @@ -158,18 +173,157 @@ template <> struct TimeScaleTraits { } }; +// ── JD-backed scale helper ─────────────────────────────────────────────────── +// Scales stored as JD days in their own time-scale: arithmetic delegates +// through JD using the raw FFI functions. from_civil / to_civil route via JD. + +namespace detail { + +/// Generic TimeScaleTraits for any scale S whose internal value is a JD-like +/// double, with jd_to_s / s_to_jd conversion functions. +/// from_civil / to_civil use deferred template parameters to avoid requiring +/// CivilTime to be complete at definition time. +template +struct JDBackedScaleTraits { + /// CivilTime → JD → target scale. + template static double from_civil(const CT &ct) { + double jd_val = TimeScaleTraits::from_civil(ct); + return JdToS(jd_val); + } + + /// Target scale → JD → CivilTime. + template static CT to_civil(double val) { + double jd_val = SToJd(val); + return TimeScaleTraits::to_civil(jd_val); + } + + static double add_days(double val, double delta) { + // Convert to JD, add, convert back. + double jd = SToJd(val); + double jd_new = tempoch_jd_add_days(jd, delta); + return JdToS(jd_new); + } + + static double difference(double a, double b) { + // Convert both to JD and subtract — preserves the day-count semantic. + return tempoch_jd_difference(SToJd(a), SToJd(b)); + } + + static qtty_quantity_t difference_qty(double a, double b) { + return tempoch_jd_difference_qty(SToJd(a), SToJd(b)); + } + + static void add_qty(double val, qtty_quantity_t duration, double &out) { + double jd = SToJd(val); + double jd_out; + check_status(tempoch_jd_add_qty(jd, duration, &jd_out), + "Time::add_qty"); + out = JdToS(jd_out); + } +}; + +} // namespace detail + +// ── TTScale ───────────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "TT"; } +}; + +// ── TAIScale ──────────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "TAI"; } +}; + +// ── TDBScale ──────────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "TDB"; } +}; + +// ── TCGScale ──────────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "TCG"; } +}; + +// ── TCBScale ──────────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "TCB"; } +}; + +// ── GPSScale ──────────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "GPS"; } +}; + +// ── UTScale ───────────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "UT1"; } +}; + +// ── JDEScale ──────────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "JDE"; } +}; + +// ── UnixTimeScale ─────────────────────────────────────────────────────────── + +template <> +struct TimeScaleTraits + : detail::JDBackedScaleTraits { + static constexpr const char *label() { return "Unix"; } +}; + // ============================================================================ // TimeConvertTraits — cross-scale conversion // ============================================================================ /** - * @brief Primary template — specialise for each supported A→B pair. + * @brief Primary template — routes any A→B conversion through JD. * - * Required: `static double convert(double src_days)`. + * Explicit specialisations for direct pairs (e.g. JD↔MJD, JD↔TDB) + * bypass this fallback. For all other pairs, the default implementation + * computes A → JD → B using the two explicitly-specialised half-paths. */ template struct TimeConvertTraits { - static_assert(sizeof(From) == 0, - "TimeConvertTraits is not specialised for this pair."); + static double convert(double src) { + double jd = TimeConvertTraits::convert(src); + return TimeConvertTraits::convert(jd); + } +}; + +/// Identity conversion — zero cost. +template struct TimeConvertTraits { + static double convert(double v) { return v; } }; // ── JD ↔ MJD ──────────────────────────────────────────────────────────────── @@ -202,4 +356,122 @@ template <> struct TimeConvertTraits { static double convert(double mjd) { return mjd; } }; +// ── JD ↔ TT ──────────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_tt(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double tt) { return tempoch_tt_to_jd(tt); } +}; + +// ── JD ↔ TAI ─────────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_tai(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double tai) { return tempoch_tai_to_jd(tai); } +}; + +// ── JD ↔ TDB ─────────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_tdb(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double tdb) { return tempoch_tdb_to_jd(tdb); } +}; + +// ── JD ↔ TCG ─────────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_tcg(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double tcg) { return tempoch_tcg_to_jd(tcg); } +}; + +// ── JD ↔ TCB ─────────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_tcb(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double tcb) { return tempoch_tcb_to_jd(tcb); } +}; + +// ── JD ↔ GPS ─────────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_gps(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double gps) { return tempoch_gps_to_jd(gps); } +}; + +// ── JD ↔ UT ──────────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_ut(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double ut) { return tempoch_ut_to_jd(ut); } +}; + +// ── JD ↔ JDE ─────────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_jde(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double jde) { return tempoch_jde_to_jd(jde); } +}; + +// ── JD ↔ UnixTime ────────────────────────────────────────────────────────── + +template <> struct TimeConvertTraits { + static double convert(double jd) { return tempoch_jd_to_unix(jd); } +}; +template <> struct TimeConvertTraits { + static double convert(double unix_t) { return tempoch_unix_to_jd(unix_t); } +}; + +// ── Generic cross-scale ──────────────────────────────────────────────────── +// For any two non-JD scales A↔B, route through JD: A → JD → B. +// (MJD/UTC conversions are handled by explicit specializations above.) + +template <> struct TimeConvertTraits { + static double convert(double m) { + return tempoch_jd_to_tt(tempoch_mjd_to_jd(m)); + } +}; +template <> struct TimeConvertTraits { + static double convert(double t) { + return tempoch_jd_to_mjd(tempoch_tt_to_jd(t)); + } +}; + +template <> struct TimeConvertTraits { + static double convert(double m) { + return tempoch_jd_to_tdb(tempoch_mjd_to_jd(m)); + } +}; +template <> struct TimeConvertTraits { + static double convert(double t) { + return tempoch_jd_to_mjd(tempoch_tdb_to_jd(t)); + } +}; + +template <> struct TimeConvertTraits { + static double convert(double m) { + return tempoch_jd_to_tai(tempoch_mjd_to_jd(m)); + } +}; +template <> struct TimeConvertTraits { + static double convert(double t) { + return tempoch_jd_to_mjd(tempoch_tai_to_jd(t)); + } +}; + } // namespace tempoch diff --git a/include/tempoch/time.hpp b/include/tempoch/time.hpp index d99cad3..748020a 100644 --- a/include/tempoch/time.hpp +++ b/include/tempoch/time.hpp @@ -24,6 +24,36 @@ using JulianDate = Time; /// Modified Julian Date — JD − 2 400 000.5. using MJD = Time; +/// Barycentric Dynamical Time. +using TDB = Time; + +/// Terrestrial Time. +using TT = Time; + +/// International Atomic Time. +using TAI = Time; + +/// Geocentric Coordinate Time. +using TCG = Time; + +/// Barycentric Coordinate Time. +using TCB = Time; + +/// GPS Time. +using GPS = Time; + +/// Universal Time (UT1). +using UT = Time; + +/// Alias — mirrors Rust's `UniversalTime`. +using UniversalTime = Time; + +/// Julian Ephemeris Date (≡ TDB expressed as JD). +using JDE = Time; + +/// Unix (POSIX) time — seconds since 1970-01-01T00:00:00 UTC. +using UnixTime = Time; + // `UTC` and `CivilTime` are already declared in time_base.hpp: // using UTC = CivilTime; diff --git a/include/tempoch/time_base.hpp b/include/tempoch/time_base.hpp index 3c6820f..83ede8f 100644 --- a/include/tempoch/time_base.hpp +++ b/include/tempoch/time_base.hpp @@ -13,6 +13,7 @@ * for `Time` via SFINAE. */ +#include "civil_time.hpp" #include "qtty/qtty.hpp" #include "scales.hpp" #include @@ -21,69 +22,6 @@ namespace tempoch { -// ============================================================================ -// CivilTime (civil UTC breakdown — formerly the `UTC` struct) -// ============================================================================ - -/** - * @brief UTC date-time breakdown. - * - * A simple value type mirroring the C `tempoch_utc_t` struct. - * - * @code - * tempoch::CivilTime noon(2000, 1, 1, 12, 0, 0); - * auto jd = tempoch::JulianDate::from_utc(noon); - * @endcode - */ -struct CivilTime { - int32_t year; ///< Gregorian year (astronomical year numbering). - uint8_t month; ///< Month [1, 12]. - uint8_t day; ///< Day of month [1, 31]. - uint8_t hour; ///< Hour [0, 23]. - uint8_t minute; ///< Minute [0, 59]. - uint8_t second; ///< Second [0, 60] (leap-second aware). - uint32_t nanosecond; ///< Nanosecond [0, 999 999 999]. - - /// Default constructor: J2000 epoch noon-like civil representation. - CivilTime() - : year(2000), month(1), day(1), hour(12), minute(0), second(0), - nanosecond(0) {} - - /** - * @brief Construct from civil UTC components. - */ - CivilTime(int32_t y, uint8_t mo, uint8_t d, uint8_t h = 0, uint8_t mi = 0, - uint8_t s = 0, uint32_t ns = 0) - : year(y), month(mo), day(d), hour(h), minute(mi), second(s), - nanosecond(ns) {} - - /// Convert to the C FFI struct. - tempoch_utc_t to_c() const { - return {year, month, day, hour, minute, second, nanosecond}; - } - - /// Create from the C FFI struct. - static CivilTime from_c(const tempoch_utc_t &c) { - return CivilTime(c.year, c.month, c.day, c.hour, c.minute, c.second, - c.nanosecond); - } -}; - -/// Stream CivilTime as YYYY-MM-DD HH:MM:SS[.nnnnnnnnn]. -inline std::ostream &operator<<(std::ostream &os, const CivilTime &u) { - const char prev = os.fill(); - os << u.year << '-' << std::setfill('0') << std::setw(2) - << static_cast(u.month) << '-' << std::setw(2) - << static_cast(u.day) << ' ' << std::setw(2) - << static_cast(u.hour) << ':' << std::setw(2) - << static_cast(u.minute) << ':' << std::setw(2) - << static_cast(u.second); - if (u.nanosecond != 0) - os << '.' << std::setw(9) << u.nanosecond; - os.fill(prev); - return os; -} - // ============================================================================ // TimeScaleTraits deferred implementations (need CivilTime to be complete) // ============================================================================ @@ -289,6 +227,15 @@ template class Time { std::enable_if_t, Time> to_jd() const { return Time(TimeConvertTraits::convert(m_days)); } + + // ── UT-only extras (SFINAE-guarded) ─────────────────────────────────── + + /// ΔT = TT − UT1 in seconds. Only available for `Time`. + template + std::enable_if_t, qtty::Second> delta_t() const { + double jd = TimeConvertTraits::convert(m_days); + return qtty::Second(tempoch_delta_t_seconds(jd)); + } }; // ============================================================================ diff --git a/run-ci.sh b/run-ci.sh new file mode 100755 index 0000000..b114e8e --- /dev/null +++ b/run-ci.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Local CI runner that mirrors .github/workflows/ci.yml + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +bold() { printf '\e[1m%s\e[0m\n' "$*"; } +error() { printf '\e[31m%s\e[0m\n' "$*" >&2; } + +run_lint() { + bold "==> Lint: configure + clang-format + clang-tidy" + rm -rf build + cmake -S . -B build -G Ninja -DTEMPOCH_BUILD_DOCS=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + + mapfile -t format_files < <(git ls-files '*.hpp' '*.cpp') + if [ ${#format_files[@]} -gt 0 ]; then + bold "Running clang-format..." + clang-format --dry-run --Werror "${format_files[@]}" + else + echo "No C++ files to format" + fi + + mapfile -t tidy_files < <(git ls-files '*.cpp') + if [ ${#tidy_files[@]} -gt 0 ]; then + bold "Running clang-tidy..." + for f in "${tidy_files[@]}"; do + echo "clang-tidy: ${f}" + clang-tidy -p build --warnings-as-errors='*' "${f}" + done + else + echo "No C++ source files for clang-tidy" + fi +} + +run_build_test_docs() { + bold "==> Build + Test + Docs" + rm -rf build + cmake -S . -B build -G Ninja -DTEMPOCH_BUILD_DOCS=ON + export CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-2} + cmake --build build --target test_tempoch + ctest --test-dir build --output-on-failure -L tempoch_cpp + cmake --build build --target docs +} + +run_coverage() { + bold "==> Coverage" + rm -rf build-coverage coverage.xml coverage_html code-coverage-results.md + cmake -S . -B build-coverage -G Ninja \ + -DTEMPOCH_BUILD_DOCS=OFF \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_FLAGS="--coverage" \ + -DCMAKE_EXE_LINKER_FLAGS="--coverage" + + cmake --build build-coverage --target test_tempoch + ctest --test-dir build-coverage --output-on-failure -L tempoch_cpp + + mkdir -p coverage_html + gcovr \ + --root . \ + --exclude 'build-coverage/.*' \ + --exclude 'tempoch/.*' \ + --exclude 'tests/.*' \ + --exclude 'examples/.*' \ + --xml \ + --output coverage.xml + + gcovr \ + --root . \ + --exclude 'build-coverage/.*' \ + --exclude 'tempoch/.*' \ + --exclude 'tests/.*' \ + --exclude 'examples/.*' \ + --html-details \ + --output coverage_html/index.html +} + +cd "${ROOT_DIR}" + +# Check required tools +bold "Checking required tools..." +for tool in cmake ninja clang-format clang-tidy cargo gcovr; do + if ! command -v "$tool" &> /dev/null; then + error "ERROR: $tool not found. Please install it." + exit 1 + fi +done + +bold "Using Rust toolchain:" +rustup show active-toolchain + +# Check submodules +bold "Checking submodules..." +if [ ! -f tempoch/Cargo.toml ]; then + error "ERROR: tempoch/Cargo.toml not found. Initialize submodules with: git submodule update --init --recursive" + exit 1 +fi +if [ ! -f tempoch/tempoch-ffi/Cargo.toml ]; then + error "ERROR: tempoch/tempoch-ffi/Cargo.toml not found. Initialize submodules with: git submodule update --init --recursive" + exit 1 +fi + +bold "Submodule revisions:" +echo "tempoch: $(git -C tempoch rev-parse HEAD) ($(git -C tempoch describe --tags --always 2>/dev/null || true))" +echo "tempoch-ffi: $(git -C tempoch/tempoch-ffi rev-parse HEAD) ($(git -C tempoch/tempoch-ffi describe --tags --always 2>/dev/null || true))" + +# Run CI steps +run_lint +run_build_test_docs +run_coverage + +bold "==> All CI steps completed successfully!" diff --git a/tempoch b/tempoch index a89a704..0c13a8b 160000 --- a/tempoch +++ b/tempoch @@ -1 +1 @@ -Subproject commit a89a70437a506a60fcd31a99464effe728dacb69 +Subproject commit 0c13a8be2f43acaec013923baa86f4b966b96295