Skip to main content

o_/
lib.rs

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