1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! The `mof` command family: catalogues and framework assembly.
use clap::Subcommand;
use serde_json::json;
use crate::format::StructureFormat;
use crate::{Cli, CliError, output};
/// Subcommands of `xtalforge mof`.
#[derive(Debug, Subcommand)]
pub(crate) enum MofAction {
/// List the topology catalogue.
Topologies,
/// List the building blocks.
Blocks,
/// Assemble a framework from a topology, node and linker.
Build {
/// Topology name.
topology: String,
/// Node block name.
node: String,
/// Linker block name.
linker: String,
/// Output path; defaults to standard output.
#[arg(long, value_name = "PATH")]
out: Option<String>,
/// Output format; defaults to the output extension or CIF.
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
}
/// Runs one `mof` action.
///
/// # Errors
///
/// Returns [`CliError`] for catalogue or builder failures.
pub(crate) fn run(cli: &Cli, action: &MofAction) -> Result<(), CliError> {
match action {
MofAction::Topologies => {
let topologies = xtalforge::mof::topologies().map_err(CliError::mof)?;
let items: Vec<serde_json::Value> = topologies
.iter()
.map(|topology| {
json!({
"name": topology.name,
"nodes": topology.nodes.len(),
"edges": topology.edges.len(),
})
})
.collect();
if cli.json {
println!("{}", json!(items));
} else {
for topology in topologies {
println!(
"{:<8} {} nodes, {} edges",
topology.name,
topology.nodes.len(),
topology.edges.len()
);
}
}
Ok(())
}
MofAction::Blocks => {
let blocks = xtalforge::mof::blocks().map_err(CliError::mof)?;
let items: Vec<serde_json::Value> = blocks
.iter()
.map(|block| {
json!({
"name": block.name,
"kind": format!("{:?}", block.kind),
"atoms": block.elements.len(),
"connections": block.connection_points.len(),
})
})
.collect();
if cli.json {
println!("{}", json!(items));
} else {
for block in blocks {
println!(
"{:<12} {:?} {} atoms, {} connections",
block.name,
block.kind,
block.elements.len(),
block.connection_points.len()
);
}
}
Ok(())
}
MofAction::Build {
topology,
node,
linker,
out,
format,
} => {
let request =
xtalforge::mof::MofRequest::new(topology.clone(), node.clone(), linker.clone());
let built = xtalforge::mof::build_mof(&request).map_err(CliError::mof)?;
if !cli.quiet {
for warning in &built.warnings {
eprintln!("warning: {warning}");
}
}
if let Some(destination) = out {
output::emit_structure(cli, &built.structure, Some(destination), *format)?;
if !cli.quiet && !cli.json {
println!(
"built {} ({}/{}) with {} warnings",
built.structure.name(),
built.topology,
built.linker,
built.warnings.len()
);
}
return Ok(());
}
if cli.json {
println!(
"{}",
json!({
"structure": built.structure,
"topology": built.topology,
"node": built.node,
"linker": built.linker,
"warnings": built.warnings,
})
);
Ok(())
} else {
output::emit_structure(cli, &built.structure, None, *format)
}
}
}
}