qsym2/interfaces/cli/
mod.rs1use std::fmt;
4use std::fs;
5use std::path::{Path, PathBuf};
6
7use clap::{Parser, Subcommand};
8use lazy_static::lazy_static;
9#[cfg(feature = "python")]
10use pyo3::prelude::*;
11use regex::Regex;
12
13use crate::auxiliary::contributors::CONTRIBUTORS;
14use crate::io::format::{log_subtitle, log_title, qsym2_output, QSym2Output};
15
16const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
18
19#[derive(Subcommand, Debug)]
25pub enum Commands {
26 Template {
28 #[arg(short, long)]
30 name: Option<PathBuf>,
31 },
32
33 Run {
35 #[arg(short, long, required = true)]
37 config: PathBuf,
38
39 #[arg(short, long, required = true)]
41 output: PathBuf,
42
43 #[arg(short, long, action = clap::ArgAction::Count)]
45 debug: u8,
46 },
47}
48
49#[derive(Parser, Debug)]
51#[command(author, version, about)]
52#[command(next_line_help = true)]
53pub struct Cli {
54 #[command(subcommand)]
56 pub command: Commands,
57}
58
59impl fmt::Display for Cli {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match &self.command {
62 Commands::Template { name } => {
63 writeln!(
64 f,
65 "Generate a template configuration YAML file: {}",
66 name.as_ref()
67 .map(|name| name.display().to_string())
68 .unwrap_or("no name specified".to_string())
69 )
70 }
71 Commands::Run {
72 config,
73 output,
74 debug,
75 } => {
76 writeln!(f, "{:<11}: {}", "Config file", config.display().to_string())?;
77 writeln!(f, "{:<11}: {}", "Output file", output.display().to_string())?;
78 writeln!(f, "{:<11}: {}", "Debug level", debug)?;
79 Ok(())
80 }
81 }
82 }
83}
84
85#[cfg_attr(feature = "python", pyfunction)]
91pub fn qsym2_output_heading() {
92 let version = if let Some(ver) = VERSION {
93 format!("v{ver}")
94 } else {
95 format!("v unknown")
96 };
97 qsym2_output!("╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮");
99 qsym2_output!("│ 222222222222222 │");
100 qsym2_output!("│ 2:::::::::::::::22 │");
101 qsym2_output!("│ 2::::::222222:::::2 │");
102 qsym2_output!("│ 2222222 2:::::2 │");
103 qsym2_output!("│ 2:::::2 │");
104 qsym2_output!("│ QQQQQQQQQ SSSSSSSSSSSSSSS 2:::::2 │");
105 qsym2_output!("│ QQ:::::::::QQ SS:::::::::::::::S 2222::::2 │");
106 qsym2_output!("│ QQ:::::::::::::QQ S:::::SSSSSS::::::S 22222::::::22 │");
107 qsym2_output!("│ Q:::::::QQQ:::::::QS:::::S SSSSSSS 22::::::::222 │");
108 qsym2_output!("│ Q::::::O Q::::::QS:::::S yyyyyyy yyyyyyy mmmmmmm mmmmmmm 2:::::22222 │");
109 qsym2_output!("│ Q:::::O Q:::::QS:::::S y:::::y y:::::ymm:::::::m m:::::::mm 2:::::2 │");
110 qsym2_output!("│ Q:::::O Q:::::Q S::::SSSS y:::::y y:::::ym::::::::::mm::::::::::m2:::::2 │");
111 qsym2_output!("│ Q:::::O Q:::::Q SS::::::SSSSS y:::::y y:::::y m::::::::::::::::::::::m2:::::2 222222 │");
112 qsym2_output!("│ Q:::::O Q:::::Q SSS::::::::SSy:::::y y:::::y m:::::mmm::::::mmm:::::m2::::::2222222:::::2 │");
113 qsym2_output!("│ Q:::::O Q:::::Q SSSSSS::::Sy:::::y y:::::y m::::m m::::m m::::m2::::::::::::::::::2 │");
114 qsym2_output!("│ Q:::::O QQQQ:::::Q S:::::Sy:::::y:::::y m::::m m::::m m::::m22222222222222222222 │");
115 qsym2_output!("│ Q::::::O Q::::::::Q S:::::S y:::::::::y m::::m m::::m m::::m │");
116 qsym2_output!("│ Q:::::::QQ::::::::QSSSSSSS S:::::S y:::::::y m::::m m::::m m::::m │");
117 qsym2_output!("│ QQ::::::::::::::Q S::::::SSSSSS:::::S y:::::y m::::m m::::m m::::m │");
118 qsym2_output!("│ QQ:::::::::::Q S:::::::::::::::SS y:::::y m::::m m::::m m::::m │");
119 qsym2_output!("│ QQQQQQQQ::::QQ SSSSSSSSSSSSSSS y:::::y mmmmmm mmmmmm mmmmmm │");
120 qsym2_output!("│ Q:::::Q y:::::y │");
121 qsym2_output!("│ QQQQQQ y:::::y A program for Quantum Symbolic Symmetry │");
122 qsym2_output!("│ y:::::y │");
123 qsym2_output!("│ y:::::y {version:>13} (2025) │");
124 qsym2_output!("│ yyyyyyy Author: Bang C. Huynh │");
125 qsym2_output!("╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯");
126 qsym2_output!(" developed with support from the ERC's topDFT project at the University of Nottingham, UK");
127 qsym2_output!("");
128 qsym2_output!(" If you find QSym² helpful in your research, please support us by citing our paper:");
129 qsym2_output!(" Huynh, B. C., Wibowo-Teale, M. & Wibowo-Teale, A. M.");
130 qsym2_output!(" *J. Chem. Theory Comput.* **20**, 114–133.");
131 qsym2_output!(" doi:10.1021/acs.jctc.3c01118 (2024)");
132 qsym2_output!("");
133}
134
135lazy_static! {
136 static ref COMMENT_RE: Regex = Regex::new(r"^\s*#.*?").expect("Regex pattern invalid.");
138}
139
140#[cfg_attr(feature = "python", pyfunction)]
142pub fn qsym2_output_contributors() {
143 qsym2_output!(" Contributors (in alphabetical order):");
144 CONTRIBUTORS.iter().for_each(|contrib| {
145 qsym2_output!(" {}", contrib.trim());
146 });
147 qsym2_output!("");
148}
149
150pub fn qsym2_output_calculation_summary<P: AsRef<Path>>(config_path: P, cli: &Cli) {
157 log_title("Calculation Summary");
158 qsym2_output!("");
159
160 log_subtitle("Command line arguments");
161 cli.log_output_display();
162 qsym2_output!("");
163
164 log_subtitle("Input YAML configuration file");
165 let config_contents =
166 fs::read_to_string(&config_path).expect("Input configuration YAML file could not be read.");
167
168 qsym2_output!("File path: {}", config_path.as_ref().display());
169 let filtered_config_contents = config_contents
170 .lines()
171 .filter_map(|line| {
172 if COMMENT_RE.is_match(line) {
173 None
174 } else {
175 Some(line.trim_end().to_string())
176 }
177 })
178 .collect::<Vec<_>>();
179 let length = filtered_config_contents
180 .iter()
181 .map(|line| line.chars().count())
182 .max()
183 .unwrap_or(20);
184 let formatted_config_contents = itertools::intersperse(
185 filtered_config_contents
186 .iter()
187 .map(|line| format!("┊ {line:<length$} ┊")),
188 "\n".to_string(),
189 )
190 .collect::<String>();
191 qsym2_output!("┌{}┐", "┄".repeat(length + 2));
192 formatted_config_contents.trim().log_output_display();
193 qsym2_output!("└{}┘", "┄".repeat(length + 2));
194 qsym2_output!("");
195 qsym2_output!("");
196}