muse2/output/
metadata.rs

1//! Write run, build and platform metadata to a TOML file.
2//!
3//! This module collects information about the current run (model path and
4//! start time), build information provided by the `built` script, and basic
5//! platform details. The aggregated metadata is written as `metadata.toml` in
6//! the output directory.
7use anyhow::Result;
8use chrono::prelude::*;
9use platform_info::{PlatformInfo, PlatformInfoAPI, UNameAPI};
10use serde::Serialize;
11use std::fs;
12use std::path::Path;
13
14/// The output filename used for metadata.
15const METADATA_FILE_NAME: &str = "metadata.toml";
16
17/// Build-time information included by the build script (via the `built` crate).
18///
19/// The `built` script produces a Rust file containing constants for package
20/// name, version, target, rustc version, build time, and optional git metadata.
21#[allow(clippy::doc_markdown)]
22#[allow(clippy::needless_raw_strings)]
23mod built_info {
24    // The file has been placed there by the build script.
25    include!(concat!(env!("OUT_DIR"), "/built.rs"));
26}
27
28/// Return a short git commit hash for the build, or `"unknown"` when not
29/// available. If the source tree was dirty at build time, `-dirty` is appended.
30fn get_git_hash() -> String {
31    let Some(hash) = built_info::GIT_COMMIT_HASH_SHORT else {
32        return "unknown".into();
33    };
34
35    if built_info::GIT_DIRTY == Some(true) {
36        format!("{hash}-dirty")
37    } else {
38        hash.into()
39    }
40}
41
42/// Top-level metadata structure serialized to TOML.
43///
44/// Contains information about the run, the program build, and the host
45/// platform. This is written to `metadata.toml` by `write_metadata`.
46#[derive(Serialize)]
47struct Metadata<'a> {
48    run: RunMetadata<'a>,
49    program: ProgramMetadata<'a>,
50    platform: PlatformMetadata,
51}
52
53/// Information about the model run
54#[derive(Serialize)]
55struct RunMetadata<'a> {
56    /// Path to the model which was run
57    model_path: &'a Path,
58    /// The date and time on which the run started
59    datetime: String,
60}
61
62impl<'a> RunMetadata<'a> {
63    fn new(model_path: &'a Path) -> Self {
64        let dt = Local::now();
65        Self {
66            model_path,
67            datetime: dt.to_rfc2822(),
68        }
69    }
70}
71
72#[derive(Serialize)]
73struct ProgramMetadata<'a> {
74    /// The program name
75    name: &'a str,
76    /// The program version as specified in Cargo.toml
77    version: &'a str,
78    /// The target architecture for the build (e.g. x86_64-unknown-linux-gnu)
79    target: &'a str,
80    /// Whether it is a debug build
81    is_debug: bool,
82    /// The version of rustc used to compile MUSE
83    rustc_version: &'a str,
84    /// When MUSE was built
85    build_time_utc: &'a str,
86    /// The git commit hash for the version of MUSE (if known)
87    git_commit_hash: String,
88}
89
90impl Default for ProgramMetadata<'_> {
91    fn default() -> Self {
92        Self {
93            name: built_info::PKG_NAME,
94            version: built_info::PKG_VERSION,
95            target: built_info::TARGET,
96            is_debug: built_info::DEBUG,
97            rustc_version: built_info::RUSTC_VERSION,
98            build_time_utc: built_info::BUILT_TIME_UTC,
99            git_commit_hash: get_git_hash(),
100        }
101    }
102}
103
104/// Information about the platform on which MUSE is running.
105///
106/// The fields correspond to different data available from the [`PlatformInfo`] struct.
107#[derive(Serialize)]
108struct PlatformMetadata {
109    sysname: String,
110    nodename: String,
111    release: String,
112    version: String,
113    machine: String,
114    osname: String,
115}
116
117impl Default for PlatformMetadata {
118    fn default() -> Self {
119        let info = PlatformInfo::new().expect("Unable to determine platform info");
120        Self {
121            sysname: info.sysname().to_string_lossy().into(),
122            nodename: info.nodename().to_string_lossy().into(),
123            release: info.release().to_string_lossy().into(),
124            version: info.version().to_string_lossy().into(),
125            machine: info.machine().to_string_lossy().into(),
126            osname: info.osname().to_string_lossy().into(),
127        }
128    }
129}
130
131/// Write metadata to `metadata.toml` in the given output directory.
132///
133/// # Arguments
134///
135/// * `output_path` - Directory where `metadata.toml` will be written.
136/// * `model_path` - Path to the model that was executed (recorded in the metadata).
137///
138/// # Errors
139///
140/// Returns an error if serializing the metadata or writing the file fails.
141pub fn write_metadata(output_path: &Path, model_path: &Path) -> Result<()> {
142    let metadata = Metadata {
143        run: RunMetadata::new(model_path),
144        program: ProgramMetadata::default(),
145        platform: PlatformMetadata::default(),
146    };
147    let file_path = output_path.join(METADATA_FILE_NAME);
148    fs::write(&file_path, toml::to_string(&metadata)?)?;
149
150    Ok(())
151}