siderust-archive 0.1.1

Reusable Rust bindings for the Siderust Archive: manifests, checksums, provenance, and runtime download of scientific datasets (IERS time data, kernels, planetary theories).
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (C) 2026 Vallés Puig, Ramon

//! Build-time pipeline for the Pluto abbreviated series (Meeus 1998, ch. 37).
//!
//! Reads `raw/pluto_tables.csv` and emits `pluto_data.rs` into `OUT_DIR`
//! with `PLUTO_ARGUMENTS`, `PLUTO_LONGITUDE_TERMS`, `PLUTO_LATITUDE_TERMS`,
//! and `PLUTO_RADIUS_TERMS`.
//!
//! ## CSV layout
//!
//! `index,j,s,p,long_a,long_b,lat_a,lat_b,rad_a,rad_b`
//!
//! `j`, `s`, `p` are Delaunay-like integer multipliers (Jupiter, Saturn,
//! Pluto longitude in years).  `*_a` / `*_b` are raw sine/cosine amplitudes
//! in 0.1 µas (longitude/latitude) or 1e-7 AU (radius).

use anyhow::{Context, Result};
use std::fmt::Write as FmtWrite;
use std::fs;
use std::path::Path;

/// Normalize an integer token to a valid Rust `f64` literal by appending `.0`
/// if no decimal point is present.
fn to_f64_lit(s: &str) -> String {
    if s.contains('.') || s.contains('e') || s.contains('E') {
        s.to_string()
    } else {
        format!("{s}.0")
    }
}

pub(crate) fn run_regen(raw_dir: &Path, out_dir: &Path) -> Result<()> {
    let csv_path = raw_dir.join("pluto_tables.csv");
    println!("cargo:rerun-if-changed={}", csv_path.display());

    let csv =
        fs::read_to_string(&csv_path).with_context(|| format!("reading {}", csv_path.display()))?;

    struct Row {
        j: String,
        s: String,
        p: String,
        long_a: String,
        long_b: String,
        lat_a: String,
        lat_b: String,
        rad_a: String,
        rad_b: String,
    }

    let mut rows: Vec<Row> = Vec::new();
    for (i, line) in csv.lines().enumerate() {
        if i == 0 {
            continue; // skip header
        }
        let line = line.trim();
        if line.is_empty() {
            continue;
        }
        let cols: Vec<&str> = line.split(',').collect();
        anyhow::ensure!(cols.len() == 10, "expected 10 columns at line {}", i + 1);
        rows.push(Row {
            j: to_f64_lit(cols[1]),
            s: to_f64_lit(cols[2]),
            p: to_f64_lit(cols[3]),
            long_a: to_f64_lit(cols[4]),
            long_b: to_f64_lit(cols[5]),
            lat_a: to_f64_lit(cols[6]),
            lat_b: to_f64_lit(cols[7]),
            rad_a: to_f64_lit(cols[8]),
            rad_b: to_f64_lit(cols[9]),
        });
    }

    let mut out = String::new();
    writeln!(
        out,
        "// AUTOGENERATED by siderust-archive build.rs — DO NOT EDIT BY HAND"
    )?;
    writeln!(out, "// Source: src/pluto/raw/pluto_tables.csv")?;
    writeln!(out)?;
    writeln!(out, "use crate::pluto::*;")?;
    writeln!(out)?;

    // PLUTO_ARGUMENTS
    writeln!(out, "#[rustfmt::skip]")?;
    writeln!(out, "pub static PLUTO_ARGUMENTS: &[PlutoArgument] = &[")?;
    for row in &rows {
        writeln!(
            out,
            "    PlutoArgument {{ j: {}, s: {}, p: {} }},",
            row.j, row.s, row.p
        )?;
    }
    writeln!(out, "];")?;
    writeln!(out)?;

    // PLUTO_LONGITUDE_TERMS
    writeln!(out, "#[rustfmt::skip]")?;
    writeln!(out, "pub static PLUTO_LONGITUDE_TERMS: &[PlutoTerm] = &[")?;
    for row in &rows {
        writeln!(
            out,
            "    PlutoTerm {{ a: {}, b: {} }},",
            row.long_a, row.long_b
        )?;
    }
    writeln!(out, "];")?;
    writeln!(out)?;

    // PLUTO_LATITUDE_TERMS
    writeln!(out, "#[rustfmt::skip]")?;
    writeln!(out, "pub static PLUTO_LATITUDE_TERMS: &[PlutoTerm] = &[")?;
    for row in &rows {
        writeln!(
            out,
            "    PlutoTerm {{ a: {}, b: {} }},",
            row.lat_a, row.lat_b
        )?;
    }
    writeln!(out, "];")?;
    writeln!(out)?;

    // PLUTO_RADIUS_TERMS
    writeln!(out, "#[rustfmt::skip]")?;
    writeln!(out, "pub static PLUTO_RADIUS_TERMS: &[PlutoTerm] = &[")?;
    for row in &rows {
        writeln!(
            out,
            "    PlutoTerm {{ a: {}, b: {} }},",
            row.rad_a, row.rad_b
        )?;
    }
    writeln!(out, "];")?;

    let out_path = out_dir.join("pluto_data.rs");
    fs::write(&out_path, &out).with_context(|| format!("writing {}", out_path.display()))?;

    Ok(())
}