Skip to content
This repository was archived by the owner on Oct 1, 2025. It is now read-only.

Commit c97e900

Browse files
committed
feat: add command runner
1 parent c2c66f5 commit c97e900

File tree

10 files changed

+286
-114
lines changed

10 files changed

+286
-114
lines changed

snap/snapcraft.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
name: oxidizr
22
adopt-info: oxidizr
3-
summary: TODO
3+
summary: A tool for experimenting with Rust-based packages on Ubuntu
44
description: |
5-
TODO
5+
A tool for enabling/disabling experimental Rust-based packages on Ubuntu.
66
77
license: Apache-2.0
88
contact: https://github.com/jnsgruk/oxidizr/issues
99
issues: https://github.com/jnsgruk/oxidizr/issues
1010
source-code: https://github.com/jnsgruk/oxidizr
1111

1212
base: core24
13-
confinement: strict
13+
confinement: classic
1414
grade: stable
1515
compression: lzo
1616

@@ -22,7 +22,9 @@ parts:
2222
oxidizr:
2323
plugin: rust
2424
source: .
25-
override-prime: |
25+
build-attributes:
26+
- enable-patchelf
27+
override-stage: |
2628
craftctl default
2729
bin_version="$($CRAFT_PRIME/bin/oxidizr --version | cut -d" " -f2)"
2830
craftctl set version="$bin_version"

src/main.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
pub mod packages;
22
pub mod utils;
33

4+
use std::sync::Arc;
5+
46
use anyhow::Result;
57
use clap::Parser;
68
use clap_verbosity_flag::{InfoLevel, Verbosity};
9+
use packages::{Experiment, RustCoreutils, RustDiffutils, RustFindutils};
10+
use tracing::info;
711
use tracing_subscriber::{fmt, prelude::*};
8-
use utils::update_package_lists;
12+
use utils::{update_package_lists, System};
913

1014
/// A command-line utility to install modern Rust-based replacements of essential
1115
/// packages such as coreutils, findutils, diffutils and sudo and make them the
@@ -34,15 +38,22 @@ fn main() -> Result<()> {
3438
"This program must be run as root"
3539
);
3640

41+
let system = Arc::new(System {});
42+
43+
let coreutils = RustCoreutils::new(system.clone());
44+
let findutils = RustFindutils::new(system.clone());
45+
let diffutils = RustDiffutils::new(system.clone());
46+
3747
if args.restore {
38-
packages::RustCoreutils::restore()?;
39-
packages::RustFindutils::restore()?;
40-
packages::RustDiffutils::restore()?;
48+
coreutils.restore()?;
49+
findutils.restore()?;
50+
diffutils.restore()?;
4151
} else {
42-
update_package_lists()?;
43-
packages::RustCoreutils::install()?;
44-
packages::RustFindutils::install()?;
45-
packages::RustDiffutils::install()?;
52+
info!("Updating apt cache");
53+
update_package_lists(system.clone())?;
54+
coreutils.install()?;
55+
findutils.install()?;
56+
diffutils.install()?;
4657
}
4758

4859
Ok(())

src/packages/coreutils.rs

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,59 @@
11
use crate::utils::{
22
install_package, list_files, release, remove_package, replace_file_with_symlink, restore_file,
3+
Worker,
34
};
45
use anyhow::Result;
5-
use std::path::PathBuf;
6-
use tracing::{debug, info, warn};
6+
use std::{path::PathBuf, sync::Arc};
7+
use tracing::{info, warn};
8+
9+
use super::Experiment;
710

811
const PACKAGE: &str = "rust-coreutils";
912
const FIRST_SUPPORTED_RELEASE: &str = "24.04";
1013

11-
pub struct RustCoreutils {}
14+
pub struct RustCoreutils {
15+
system: Arc<dyn Worker>,
16+
}
1217

1318
impl RustCoreutils {
14-
pub fn install() -> Result<()> {
19+
fn installed() -> bool {
20+
list_files("/usr/lib/cargo/bin/coreutils").is_ok()
21+
}
22+
23+
fn compatible() -> bool {
24+
match release() {
25+
Ok(codename) => codename.as_str() >= FIRST_SUPPORTED_RELEASE,
26+
Err(_) => false,
27+
}
28+
}
29+
}
30+
31+
impl Experiment for RustCoreutils {
32+
fn new(system: Arc<dyn Worker>) -> Self {
33+
Self { system }
34+
}
35+
36+
fn install(&self) -> Result<()> {
1537
if !Self::compatible() {
1638
warn!("Skipping '{PACKAGE}'. Minimum supported release is {FIRST_SUPPORTED_RELEASE}.");
1739
return Ok(());
1840
}
1941

2042
info!("Installing and configuring {}", PACKAGE);
2143

22-
install_package(PACKAGE)?;
44+
install_package(self.system.clone(), PACKAGE)?;
2345

24-
let files = list_files("/usr/lib/cargo/bin/coreutils")?;
46+
let files = self.system.list_files("/usr/lib/cargo/bin/coreutils")?;
2547

2648
for f in files {
2749
let filename = f.file_name().unwrap();
2850
let existing = PathBuf::from("/usr/bin").join(filename);
2951
replace_file_with_symlink(PathBuf::from("/usr/bin/coreutils"), existing.clone())?;
30-
31-
debug!(
32-
"Replaced {} with symlink to {}",
33-
existing.display(),
34-
f.display()
35-
);
3652
}
3753
Ok(())
3854
}
3955

40-
pub fn restore() -> Result<()> {
56+
fn restore(&self) -> Result<()> {
4157
if !Self::installed() || !Self::compatible() {
4258
warn!("{PACKAGE} not found, skipping restore");
4359
return Ok(());
@@ -53,19 +69,42 @@ impl RustCoreutils {
5369
restore_file(existing)?;
5470
}
5571

56-
remove_package(PACKAGE)?;
72+
remove_package(self.system.clone(), PACKAGE)?;
5773

5874
Ok(())
5975
}
76+
}
6077

61-
fn installed() -> bool {
62-
list_files("/usr/lib/cargo/bin/coreutils").is_ok()
63-
}
64-
65-
fn compatible() -> bool {
66-
match release() {
67-
Ok(codename) => codename.as_str() >= FIRST_SUPPORTED_RELEASE,
68-
Err(_) => false,
78+
#[cfg(test)]
79+
mod tests {
80+
use super::*;
81+
use crate::utils::{tests::MockSystem, Command};
82+
83+
#[test]
84+
fn test_coreutils_install() -> Result<()> {
85+
let runner = Arc::new(MockSystem::new());
86+
let coreutils = RustCoreutils::new(runner.clone());
87+
88+
let mock_files = &[
89+
"/usr/lib/cargo/bin/coreutils/date",
90+
"/usr/lib/cargo/bin/coreutils/sort",
91+
"/usr/lib/cargo/bin/diffutils/diff",
92+
"/usr/lib/share/foo",
93+
];
94+
95+
for file in mock_files {
96+
runner.mock_file(PathBuf::from(file), "");
6997
}
98+
99+
// assert!(coreutils.install().is_ok());
100+
let cmd = Command::build("lsb_release", &["-cs"]);
101+
runner.run(&cmd)?;
102+
103+
assert_eq!(
104+
runner.commands.clone().into_inner(),
105+
vec!("lsb_release -cs")
106+
);
107+
108+
Ok(())
70109
}
71110
}

src/packages/diffutils.rs

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,46 @@
11
use crate::utils::{
22
install_package, list_files, release, remove_package, replace_file_with_symlink, restore_file,
3+
Worker,
34
};
45
use anyhow::Result;
5-
use std::path::PathBuf;
6+
use std::{path::PathBuf, sync::Arc};
67
use tracing::{info, warn};
78

9+
use super::Experiment;
10+
811
const PACKAGE: &str = "rust-diffutils";
912
const FIRST_SUPPORTED_RELEASE: &str = "24.10";
10-
pub struct RustDiffutils {}
13+
pub struct RustDiffutils {
14+
system: Arc<dyn Worker>,
15+
}
1116

1217
impl RustDiffutils {
13-
pub fn install() -> Result<()> {
18+
fn installed() -> bool {
19+
list_files("/usr/lib/cargo/bin/diffutils").is_ok()
20+
}
21+
22+
fn compatible() -> bool {
23+
match release() {
24+
Ok(codename) => codename.as_str() >= FIRST_SUPPORTED_RELEASE,
25+
Err(_) => false,
26+
}
27+
}
28+
}
29+
30+
impl Experiment for RustDiffutils {
31+
fn new(system: Arc<dyn Worker>) -> Self {
32+
Self { system }
33+
}
34+
35+
fn install(&self) -> Result<()> {
1436
if !Self::compatible() {
1537
warn!("Skipping '{PACKAGE}'. Minimum supported release is {FIRST_SUPPORTED_RELEASE}.");
1638
return Ok(());
1739
}
1840

1941
info!("Installing and configuring {}", PACKAGE);
2042

21-
install_package(PACKAGE)?;
43+
install_package(self.system.clone(), PACKAGE)?;
2244

2345
let files = list_files("/usr/lib/cargo/bin/diffutils")?;
2446

@@ -30,7 +52,7 @@ impl RustDiffutils {
3052
Ok(())
3153
}
3254

33-
pub fn restore() -> Result<()> {
55+
fn restore(&self) -> Result<()> {
3456
if !Self::installed() || !Self::compatible() {
3557
warn!("{PACKAGE} not found, skipping restore");
3658
return Ok(());
@@ -46,19 +68,8 @@ impl RustDiffutils {
4668
restore_file(existing)?;
4769
}
4870

49-
remove_package(PACKAGE)?;
71+
remove_package(self.system.clone(), PACKAGE)?;
5072

5173
Ok(())
5274
}
53-
54-
fn installed() -> bool {
55-
list_files("/usr/lib/cargo/bin/diffutils").is_ok()
56-
}
57-
58-
fn compatible() -> bool {
59-
match release() {
60-
Ok(codename) => codename.as_str() >= FIRST_SUPPORTED_RELEASE,
61-
Err(_) => false,
62-
}
63-
}
6475
}

src/packages/findutils.rs

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,47 @@
11
use crate::utils::{
22
install_package, list_files, release, remove_package, replace_file_with_symlink, restore_file,
3+
Worker,
34
};
45
use anyhow::Result;
5-
use std::path::PathBuf;
6+
use std::{path::PathBuf, sync::Arc};
67
use tracing::{info, warn};
78

9+
use super::Experiment;
10+
811
const PACKAGE: &str = "rust-findutils";
912
const FIRST_SUPPORTED_RELEASE: &str = "24.04";
1013

11-
pub struct RustFindutils {}
14+
pub struct RustFindutils {
15+
system: Arc<dyn Worker>,
16+
}
1217

1318
impl RustFindutils {
14-
pub fn install() -> Result<()> {
19+
fn installed() -> bool {
20+
list_files("/usr/lib/cargo/bin/findutils").is_ok()
21+
}
22+
23+
fn compatible() -> bool {
24+
match release() {
25+
Ok(codename) => codename.as_str() >= FIRST_SUPPORTED_RELEASE,
26+
Err(_) => false,
27+
}
28+
}
29+
}
30+
31+
impl Experiment for RustFindutils {
32+
fn new(system: Arc<dyn Worker>) -> Self {
33+
Self { system }
34+
}
35+
36+
fn install(&self) -> Result<()> {
1537
if !Self::compatible() {
1638
warn!("Skipping '{PACKAGE}'. Minimum supported release is {FIRST_SUPPORTED_RELEASE}.");
1739
return Ok(());
1840
}
1941

2042
info!("Installing and configuring {}", PACKAGE);
2143

22-
install_package(PACKAGE)?;
44+
install_package(self.system.clone(), PACKAGE)?;
2345

2446
let files = list_files("/usr/lib/cargo/bin/findutils")?;
2547

@@ -31,7 +53,7 @@ impl RustFindutils {
3153
Ok(())
3254
}
3355

34-
pub fn restore() -> Result<()> {
56+
fn restore(&self) -> Result<()> {
3557
if !Self::installed() || !Self::compatible() {
3658
warn!("{PACKAGE} not found, skipping restore");
3759
return Ok(());
@@ -47,19 +69,8 @@ impl RustFindutils {
4769
restore_file(existing)?;
4870
}
4971

50-
remove_package(PACKAGE)?;
72+
remove_package(self.system.clone(), PACKAGE)?;
5173

5274
Ok(())
5375
}
54-
55-
fn installed() -> bool {
56-
list_files("/usr/lib/cargo/bin/findutils").is_ok()
57-
}
58-
59-
fn compatible() -> bool {
60-
match release() {
61-
Ok(codename) => codename.as_str() >= FIRST_SUPPORTED_RELEASE,
62-
Err(_) => false,
63-
}
64-
}
6576
}

src/packages/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@ mod coreutils;
22
mod diffutils;
33
mod findutils;
44

5+
use std::sync::Arc;
6+
57
pub use coreutils::RustCoreutils;
68
pub use diffutils::RustDiffutils;
79
pub use findutils::RustFindutils;
10+
11+
use anyhow::Result;
12+
13+
use crate::utils::Worker;
14+
15+
pub trait Experiment {
16+
fn new(system: Arc<dyn Worker>) -> Self;
17+
fn install(&self) -> Result<()>;
18+
fn restore(&self) -> Result<()>;
19+
}

0 commit comments

Comments
 (0)