Skip to main content

xtalforge_agent/
capabilities.rs

1//! Runtime capabilities (Task 8.1.1).
2//!
3//! An assistant that quotes an engine from memory will name one that is not
4//! installed here. This module answers what **this** installation can do by
5//! probing it: compiled features, external binaries on `PATH`, embedded data
6//! bundles and the registered formats.
7
8use std::collections::BTreeMap;
9
10use serde::{Deserialize, Serialize};
11use xtalforge_ff::{EngineKind, EngineStatus, probe};
12
13use crate::verbs;
14
15/// Availability of one external engine.
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct EngineAvailability {
18    /// Engine name as used by the force-field API.
19    pub name: String,
20    /// Whether the binary answered.
21    pub available: bool,
22    /// Path when available, or the reason it is not.
23    pub detail: String,
24}
25
26/// Availability of one embedded data bundle.
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct BundleAvailability {
29    /// Bundle name.
30    pub name: String,
31    /// Whether it is embedded in this build.
32    pub available: bool,
33    /// What it contains.
34    pub detail: String,
35}
36
37/// What this installation can do, asked at runtime.
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub struct Capabilities {
40    /// Crate version.
41    pub version: String,
42    /// Compiled features of the agent layer.
43    pub features: BTreeMap<String, bool>,
44    /// External engines and whether they answer on `PATH`.
45    pub engines: Vec<EngineAvailability>,
46    /// Embedded data bundles.
47    pub bundles: Vec<BundleAvailability>,
48    /// Verbs an assistant can call.
49    pub verbs: Vec<String>,
50    /// Readable and writable formats.
51    pub formats: Vec<FormatAvailability>,
52}
53
54/// One registered format and the directions it supports.
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56pub struct FormatAvailability {
57    /// Stable format name.
58    pub name: String,
59    /// Lower-case extensions.
60    pub extensions: Vec<String>,
61    /// A reader exists.
62    pub readable: bool,
63    /// A writer exists.
64    pub writable: bool,
65}
66
67fn engine(kind: EngineKind) -> EngineAvailability {
68    let status = probe(kind);
69    match status {
70        EngineStatus::Available { path, version } => EngineAvailability {
71            name: kind.binary().to_owned(),
72            available: true,
73            detail: format!("{path} ({version})"),
74        },
75        EngineStatus::Missing { reason } => EngineAvailability {
76            name: kind.binary().to_owned(),
77            available: false,
78            detail: reason,
79        },
80    }
81}
82
83fn rcsr_bundle() -> BundleAvailability {
84    match xtalforge_analysis::RcsrIndex::from_embedded() {
85        Ok(index) => BundleAvailability {
86            name: "rcsr".to_owned(),
87            available: true,
88            detail: format!("{} RCSR nets, release 2019-06-01", index.len()),
89        },
90        Err(error) => BundleAvailability {
91            name: "rcsr".to_owned(),
92            available: false,
93            detail: error.to_string(),
94        },
95    }
96}
97
98/// Probes the installation and returns its capabilities.
99#[must_use]
100pub fn capability_report() -> Capabilities {
101    let mut features = BTreeMap::new();
102    features.insert("parallel".to_owned(), cfg!(feature = "parallel"));
103    features.insert("sci-form".to_owned(), cfg!(feature = "sci-form"));
104    features.insert("ml".to_owned(), cfg!(feature = "ml"));
105    features.insert("external".to_owned(), cfg!(feature = "external"));
106    let formats = xtalforge_io::registry::FORMATS
107        .iter()
108        .map(|format| FormatAvailability {
109            name: format.name.to_owned(),
110            extensions: format
111                .extensions
112                .iter()
113                .map(|extension| (*extension).to_owned())
114                .collect(),
115            readable: format.read.is_some(),
116            writable: format.write.is_some(),
117        })
118        .collect();
119    Capabilities {
120        version: xtalforge_core::version().to_owned(),
121        features,
122        engines: vec![
123            engine(EngineKind::DftbPlus),
124            engine(EngineKind::Xtb),
125            engine(EngineKind::Orca),
126        ],
127        bundles: vec![rcsr_bundle()],
128        verbs: verbs::names()
129            .iter()
130            .map(|name| (*name).to_owned())
131            .collect(),
132        formats,
133    }
134}