Skip to main content

o_/
lib.rs

1pub mod app_error;
2pub mod args;
3pub mod binengine;
4pub mod conf;
5pub mod lock;
6pub mod pm;
7pub mod report;
8pub mod toolchain;
9pub mod x;
10
11pub use app_error::AppError;
12use args::Commands;
13use home::home_dir;
14pub use o_core::engine;
15use o_core::engine::JSEngine;
16pub use o_core::error;
17use std::fs;
18use std::path::PathBuf;
19
20use crate::args::ToolChainCommand;
21use crate::binengine::BinEngine;
22use crate::pm::global_install;
23use crate::report::Report;
24
25pub fn process(args: Commands, toolchain: Option<&str>) -> Result<(), AppError> {
26    match args {
27        Commands::Run { path } => {
28            let toolchain_name = toolchain.unwrap_or("").trim().to_string();
29            let selected_toolchain = select_toolchain(&toolchain_name)?;
30            run(&path, &toolchain_name, selected_toolchain)
31        }
32        Commands::Toolchain { command } => {
33            let report = run_toolchain(command)?;
34            report::print(&report);
35            Ok(())
36        }
37        Commands::Install { global, package } => {
38            if global {
39                let report =
40                    global_install(package.as_deref()).map_err(AppError::PackageManager)?;
41                report::print(&report);
42                Ok(())
43            } else {
44                let report = pm::install().map_err(AppError::PackageManager)?;
45                report::print(&report);
46                Ok(())
47            }
48        }
49        Commands::Uninstall { name } => {
50            let report = pm::uninstall(&name).map_err(AppError::PackageManager)?;
51            report::print(&report);
52            Ok(())
53        }
54    }
55}
56
57fn run_toolchain(command: ToolChainCommand) -> Result<Report, AppError> {
58    match command {
59        ToolChainCommand::Add { user, repo } => {
60            let mut installed =
61                toolchain::install("github.com", &user, &repo).map_err(|source| {
62                    AppError::InstallToolchain {
63                        user: user.clone(),
64                        repo: repo.clone(),
65                        source: source.to_string(),
66                    }
67                })?;
68            installed.push("bin");
69            installed.push(&repo);
70            let mut target = home_dir().ok_or(AppError::HomeDirUnavailable)?;
71            target.push(".config");
72            target.push("o-");
73            target.push("toolchains");
74            target.push("bin");
75            target.push(&repo);
76            if let Some(parent) = target.parent() {
77                fs::create_dir_all(parent).map_err(|source| AppError::CreateDir {
78                    path: parent.to_path_buf(),
79                    source,
80                })?;
81            }
82            fs::rename(&installed, &target).map_err(|source| AppError::MoveToolchain {
83                from: installed.clone(),
84                to: target.clone(),
85                source,
86            })?;
87
88            Ok(Report::new(format!("installed toolchain `{repo}`"))
89                .detail(format!("source: {user}/{repo}"))
90                .detail(format!("binary: {}", target.display())))
91        }
92        ToolChainCommand::Remove { toolchain } => {
93            let mut target = home::home_dir().ok_or(AppError::HomeDirUnavailable)?;
94            target.push(".config");
95            target.push("o-");
96            target.push("toolchains");
97            target.push("bin");
98            target.push(&toolchain);
99            fs::remove_file(&target).map_err(|source| AppError::RemoveToolchain {
100                path: target.clone(),
101                source,
102            })?;
103
104            Ok(Report::new(format!("removed toolchain `{toolchain}`"))
105                .detail(format!("path: {}", target.display())))
106        }
107    }
108}
109
110fn select_toolchain(toolchain: &str) -> Result<Box<dyn JSEngine>, AppError> {
111    let toolchain = toolchain.trim();
112    if toolchain.is_empty() {
113        return Err(AppError::MissingToolchainSelection);
114    }
115
116    let path = resolve_toolchain_path(toolchain)?;
117    if !path.is_file() {
118        return Err(AppError::ToolchainNotInstalled {
119            toolchain: toolchain.to_string(),
120            path,
121        });
122    }
123
124    Ok(Box::new(BinEngine::new(
125        path.to_string_lossy().into_owned(),
126    )))
127}
128
129fn run(path: &str, toolchain_name: &str, toolchain: Box<dyn JSEngine>) -> Result<(), AppError> {
130    let file = match fs::read_to_string(path) {
131        Ok(file) => file,
132        Err(source) => {
133            return Err(AppError::ReadScript {
134                path: PathBuf::from(path),
135                source,
136            });
137        }
138    };
139
140    if let Err(err) = toolchain.run(&file, path) {
141        return Err(AppError::ToolchainExecution {
142            toolchain: toolchain_name.to_string(),
143            message: err.to_string(),
144        });
145    }
146
147    Ok(())
148}
149
150fn resolve_toolchain_path(name: &str) -> Result<PathBuf, AppError> {
151    let mut toolchain: PathBuf = home_dir().ok_or(AppError::HomeDirUnavailable)?;
152    toolchain.push(".config");
153    toolchain.push("o-");
154    toolchain.push("toolchains");
155    toolchain.push("bin");
156    toolchain.push(name);
157    Ok(toolchain)
158}