Skip to content

Commit bd4a66e

Browse files
committed
Refactor to use run module and add config CLI
- Add run module with ContainerEngine enum and run function - Store pixi as Cow<'static, str> and adjust API - Add util::dirs with cache_root - Extend CLI with Config set of commands (Show/Get/Set/Unset/Edit/Path) - Update imports and module structure to use crate::app and crate::run
1 parent e5cdb1b commit bd4a66e

File tree

9 files changed

+147
-67
lines changed

9 files changed

+147
-67
lines changed

src/app.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::{borrow::Cow, collections::HashMap};
22

33
use clap::ValueEnum;
44

@@ -69,7 +69,7 @@ pub struct RunSpec {
6969

7070
pub mounts: HashMap<MountRole, String>,
7171

72-
pub pixi: Option<String>,
72+
pub pixi: Option<Cow<'static, str>>,
7373
}
7474

7575
impl RunSpec {
@@ -90,7 +90,7 @@ impl RunSpec {
9090
self
9191
}
9292

93-
pub fn pixi(mut self, p: impl Into<String>) -> Self {
93+
pub fn pixi(mut self, p: impl Into<Cow<'static, str>>) -> Self {
9494
self.pixi = Some(p.into());
9595
self
9696
}

src/executor.rs

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,20 @@ mod native;
55
use std::path::{Path, PathBuf};
66

77
use anyhow::Result;
8-
use yansi::Paint;
98

10-
use super::App;
119
use crate::{
12-
ContainerEngine,
13-
app::{Image, RunSpec},
10+
app::{App, Image, RunSpec},
11+
run::ContainerEngine,
1412
};
1513

1614
pub struct Executor {
17-
#[allow(dead_code)]
1815
app: App,
1916
working_dir: PathBuf,
2017
engine: ContainerEngine,
2118
}
2219

2320
impl Executor {
24-
fn new(app: App, engine: ContainerEngine, working_dir: PathBuf) -> Self {
21+
pub fn new(app: App, engine: ContainerEngine, working_dir: PathBuf) -> Self {
2522
Executor {
2623
app,
2724
working_dir,
@@ -52,29 +49,6 @@ impl Executor {
5249
// }
5350
}
5451

55-
pub fn run(
56-
app: &App,
57-
app_args: Vec<String>,
58-
container_engine: &ContainerEngine,
59-
working_dir: PathBuf,
60-
) -> Result<()> {
61-
println!(
62-
"Running app: {} in directory: {}{}",
63-
app.green(),
64-
working_dir.display(),
65-
if app_args.is_empty() {
66-
"".into()
67-
} else {
68-
format!(
69-
" with arguments: {}",
70-
format!("{:?}", app_args).bright_blue()
71-
)
72-
}
73-
);
74-
75-
Executor::new(app.to_owned(), *container_engine, working_dir).execute(app.run_spec(app_args))
76-
}
77-
7852
struct Telemetry {
7953
working_dir: PathBuf,
8054
prefix: String,

src/executor/docker.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ use anyhow::Result;
44
use yansi::Paint;
55

66
use crate::{
7-
ContainerEngine,
87
app::{MountRole, RunSpec},
98
executor::{Executor, Telemetry},
10-
util,
9+
run, util,
1110
};
1211

1312
impl Executor {
1413
pub(super) fn execute_with_docker(&self, spec: RunSpec) -> Result<()> {
15-
assert!(matches!(self.engine, ContainerEngine::Docker));
14+
assert!(matches!(self.engine, run::ContainerEngine::Docker));
1615

1716
util::Command::shell(format!(
1817
"docker image inspect {0} >/dev/null 2>&1 || docker image pull {0}",

src/executor/hpc_container.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use std::{env::home_dir, fs, path::PathBuf};
1+
use std::{fs, path::PathBuf};
22

33
use crate::{
4-
ContainerEngine,
54
app::{MountRole, RunSpec},
65
executor::{Executor, Image, Telemetry},
7-
util::{self, Command},
6+
run,
7+
util::{self, Command, dirs},
88
};
99

1010
use anyhow::Result;
@@ -19,7 +19,7 @@ impl Executor {
1919
pub(super) fn execute_with_hpc_container_engine(&self, spec: RunSpec) -> Result<()> {
2020
assert!(matches!(
2121
self.engine,
22-
ContainerEngine::Singularity | ContainerEngine::Apptainer
22+
run::ContainerEngine::Singularity | run::ContainerEngine::Apptainer
2323
));
2424

2525
//self.log_execute_info(&spec);
@@ -112,7 +112,7 @@ impl Executor {
112112
}
113113

114114
fn images_root(&self) -> PathBuf {
115-
let root = home_dir().unwrap().join(".cache/rosettacommons/rc/hpc");
115+
let root = dirs::cache_root().join("hpc");
116116
std::fs::create_dir_all(&root).unwrap();
117117
root
118118
}

src/executor/native.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ use anyhow::anyhow;
44
use home::home_dir;
55
use yansi::Paint;
66

7+
use crate::run;
78
use crate::util::Command;
89
use crate::util::ensure_dir_signature;
9-
use crate::{ContainerEngine, app::RunSpec, executor::Executor};
10+
use crate::{app::RunSpec, executor::Executor};
1011

1112
impl Executor {
1213
pub(super) fn execute_native(&self, spec: RunSpec) -> Result<()> {
13-
assert!(matches!(self.engine, ContainerEngine::None));
14+
assert!(matches!(self.engine, run::ContainerEngine::None));
1415

1516
let recipe = spec
1617
.pixi
@@ -23,21 +24,25 @@ impl Executor {
2324
.unwrap()
2425
.join(format!(".cache/rosettacommons/rc/native/{}.pixi", self.app));
2526

26-
ensure_dir_signature(&pixi_evn_root, &[&spec.image.0, &recipe], |d| {
27-
std::fs::write(d.join("pixi.toml"), &recipe)?;
28-
Command::new("pixi")
29-
.cd(d)
30-
.arg("run")
31-
.arg("setup")
32-
.live()
33-
.exec()?;
34-
Ok(())
35-
})?;
27+
ensure_dir_signature(
28+
&pixi_evn_root,
29+
&[spec.image.0.as_ref(), recipe.as_ref()],
30+
|d| {
31+
std::fs::write(d.join("pixi.toml"), recipe.as_ref())?;
32+
Command::new("pixi")
33+
.cd(d)
34+
.arg("run")
35+
.arg("setup")
36+
.live()
37+
.exec()?;
38+
Ok(())
39+
},
40+
)?;
3641

3742
Ok(())
3843
}
3944

40-
/// Check if Pixi is installed
45+
/// Check if Pixi is installed, fail if not
4146
fn check_if_pixi_is_installed() -> Result<()> {
4247
match which::which("pixi") {
4348
Ok(_) => Ok(()),

src/main.rs

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
mod app;
22
mod executor;
3+
mod run;
34
mod util;
45

5-
use std::{path::PathBuf, process};
6+
use std::path::PathBuf;
7+
use std::process;
68

79
use anyhow::Result;
8-
use clap::{Parser, Subcommand, ValueEnum};
10+
use clap::{Parser, Subcommand};
911
use yansi::Paint;
1012

11-
use app::App;
13+
use crate::app::App;
1214

1315
/// A command line tool to run various Rosetta applications
1416
#[derive(Parser, Debug)]
@@ -27,15 +29,6 @@ struct Args {
2729
verbose: bool,
2830
}
2931

30-
#[derive(ValueEnum, Clone, Copy, Debug, strum::Display)]
31-
#[strum(serialize_all = "lowercase")] // "kebab-case"
32-
enum ContainerEngine {
33-
Docker,
34-
Singularity,
35-
Apptainer,
36-
None,
37-
}
38-
3932
#[derive(Subcommand, Debug)]
4033
enum Commands {
4134
/// Clean an app installation
@@ -67,8 +60,65 @@ enum Commands {
6760
working_dir: Option<PathBuf>,
6861

6962
#[arg(short = 'e', long, default_value = "docker")]
70-
container_engine: ContainerEngine,
63+
container_engine: run::ContainerEngine,
64+
},
65+
66+
Config {
67+
#[command(subcommand)]
68+
config_command: ConfigCmd,
69+
},
70+
}
71+
72+
#[derive(Subcommand, Debug)]
73+
enum ConfigCmd {
74+
/// Show the effective configuration
75+
Show(ConfigShowArgs),
76+
77+
/// Get a single configuration value
78+
Get {
79+
/// Dotted key path, e.g. `cache.root`
80+
key: String,
81+
82+
/// Output as JSON (useful for scripting)
83+
#[arg(long)]
84+
json: bool,
85+
},
86+
87+
/// Set a configuration value
88+
Set {
89+
/// Dotted key path, e.g. `cache.root`
90+
key: String,
91+
92+
/// Value to set (stringly-typed; you parse/validate per key)
93+
value: String,
7194
},
95+
96+
/// Remove a configuration override (fall back to defaults)
97+
Unset {
98+
/// Dotted key path, e.g. `cache.root`
99+
key: String,
100+
},
101+
102+
/// Open the config file in $EDITOR
103+
Edit,
104+
105+
/// Print the config file path
106+
Path,
107+
}
108+
109+
#[derive(clap::Args, Debug)]
110+
struct ConfigShowArgs {
111+
/// Output as JSON (useful for scripting)
112+
#[arg(long)]
113+
json: bool,
114+
115+
/// Output as TOML (optional; pick what you support)
116+
#[arg(long, conflicts_with = "json")]
117+
toml: bool,
118+
119+
/// Include where each value came from (defaults/env/file)
120+
#[arg(long)]
121+
origin: bool,
72122
}
73123

74124
fn main() -> Result<()> {
@@ -100,8 +150,12 @@ fn main() -> Result<()> {
100150
.canonicalize()
101151
.unwrap();
102152

103-
executor::run(app, app_args.clone(), container_engine, working_dir)
153+
run::run(app, app_args.clone(), container_engine, working_dir)
104154
}
155+
Some(Commands::Config { config_command: _ }) => {
156+
todo!();
157+
}
158+
105159
None => {
106160
eprintln!("Error: No command specified");
107161
eprintln!("Use --help to see available commands");

src/run.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use std::path::PathBuf;
2+
3+
use anyhow::Result;
4+
use clap::ValueEnum;
5+
use yansi::Paint;
6+
7+
use crate::{app, executor};
8+
9+
#[derive(ValueEnum, Clone, Copy, Debug, strum::Display)]
10+
#[strum(serialize_all = "lowercase")] // "kebab-case"
11+
pub enum ContainerEngine {
12+
Docker,
13+
Singularity,
14+
Apptainer,
15+
None,
16+
}
17+
18+
pub fn run(
19+
app: &app::App,
20+
app_args: Vec<String>,
21+
container_engine: &ContainerEngine,
22+
working_dir: PathBuf,
23+
) -> Result<()> {
24+
println!(
25+
"Running app: {} in directory: {}{}",
26+
app.green(),
27+
working_dir.display(),
28+
if app_args.is_empty() {
29+
"".into()
30+
} else {
31+
format!(
32+
" with arguments: {}",
33+
format!("{:?}", app_args).bright_blue()
34+
)
35+
}
36+
);
37+
38+
executor::Executor::new(app.to_owned(), *container_engine, working_dir)
39+
.execute(app.run_spec(app_args))
40+
}

src/util.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
mod command;
44
mod dir_guard;
5+
pub mod dirs;
56
mod yansi;
67

78
use std::io::{self, Write};

src/util/dirs.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use std::path::PathBuf;
2+
3+
use home::home_dir;
4+
5+
pub fn cache_root() -> PathBuf {
6+
home_dir().unwrap().join(".cache/rosettacommons/rc")
7+
}

0 commit comments

Comments
 (0)