diff --git a/Package/rust/.gitignore b/Package/rust/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/Package/rust/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Package/rust/Cargo.toml b/Package/rust/Cargo.toml new file mode 100644 index 0000000..507cf0e --- /dev/null +++ b/Package/rust/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "bellande_step" +version = "0.1.0" +edition = "2021" +authors = ["Bellande Robotics Sensors Research Innovation Center"] +description = "A tool for running Bellande Step calculations via API or local executable" +license = "GPL-3.0-or-later" +repository = "https://github.com/Robotics-Sensors/bellande_step" +readme = "README.md" +keywords = ["robotics", "algorithms", "research"] +categories = ["science", "algorithms"] + +[dependencies] +reqwest = { version = "0.11", features = ["json"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +structopt = "0.3" +tokio = { version = "1.0", features = ["full"] } +thiserror = "1.0" +anyhow = "1.0" + +[lib] +name = "bellande_step" +path = "src/bellande_step.rs" diff --git a/Package/rust/publish.sh b/Package/rust/publish.sh new file mode 100644 index 0000000..5b14e9b --- /dev/null +++ b/Package/rust/publish.sh @@ -0,0 +1,3 @@ +cp ../../README.md . +bash ../../git_scripts/push.sh +cargo publish diff --git a/Package/rust/src/bellande_step.rs b/Package/rust/src/bellande_step.rs new file mode 100644 index 0000000..0f5e789 --- /dev/null +++ b/Package/rust/src/bellande_step.rs @@ -0,0 +1,161 @@ +// Copyright (C) 2024 Bellande Robotics Sensors Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use reqwest; +use serde_json::{json, Value}; +use std::error::Error; +use std::path::{Path, PathBuf}; +use std::process::{self, Command}; +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "bellande_step", about = "Bellande Step Tool")] +struct Opt { + #[structopt(long, help = "First coordinate as JSON-formatted list")] + node0: String, + + #[structopt(long, help = "Second coordinate as JSON-formatted list")] + node1: String, + + #[structopt(long, help = "Limit for the algorithm")] + limit: i32, + + #[structopt(long, help = "Number of dimensions")] + dimensions: i32, + + #[structopt(long, help = "Use local executable instead of API")] + use_executable: bool, +} + +async fn make_bellande_step_request( + node0: Value, + node1: Value, + limit: i32, + dimensions: i32, +) -> Result> { + let client = reqwest::Client::new(); + let url = "https://bellande-robotics-sensors-research-innovation-center.org/api/Bellande_Step/bellande_step"; + + let payload = json!({ + "node0": node0, + "node1": node1, + "limit": limit, + "dimensions": dimensions, + "auth": { + "authorization_key": "bellande_web_api_opensource" + } + }); + + let response = client + .post(url) + .header("accept", "application/json") + .header("Content-Type", "application/json") + .json(&payload) + .send() + .await? + .json::() + .await?; + + Ok(response) +} + +fn get_executable_path() -> PathBuf { + if cfg!(target_os = "windows") { + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("Bellande_Step.exe") + } else { + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("Bellande_Step") + } +} + +fn run_bellande_step_executable( + node0: &str, + node1: &str, + limit: i32, + dimensions: i32, +) -> Result<(), Box> { + let executable_path = get_executable_path(); + let passcode = "bellande_step_executable_access_key"; + + // Parse and validate input + let node0_list: Value = serde_json::from_str(node0)?; + let node1_list: Value = serde_json::from_str(node1)?; + + // Validate dimensions + if let (Some(n0), Some(n1)) = (node0_list.as_array(), node1_list.as_array()) { + if n0.len() != dimensions as usize || n1.len() != dimensions as usize { + return Err(format!("Coordinates must have {} dimensions", dimensions).into()); + } + } + + // Prepare and run command + let output = Command::new(executable_path) + .args(&[ + passcode, + &node0, + &node1, + &limit.to_string(), + &dimensions.to_string(), + ]) + .output()?; + + if output.status.success() { + println!("{}", String::from_utf8_lossy(&output.stdout)); + Ok(()) + } else { + Err(format!( + "Process failed: {}", + String::from_utf8_lossy(&output.stderr) + ).into()) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let opt = Opt::from_args(); + + // Parse JSON strings to Values for validation + let node0: Value = serde_json::from_str(&opt.node0) + .map_err(|e| format!("Error parsing node0: {}", e))?; + let node1: Value = serde_json::from_str(&opt.node1) + .map_err(|e| format!("Error parsing node1: {}", e))?; + + if opt.use_executable { + // Run using local executable + if let Err(e) = run_bellande_step_executable( + &opt.node0, + &opt.node1, + opt.limit, + opt.dimensions, + ) { + eprintln!("Error: {}", e); + process::exit(1); + } + } else { + // Run using API + match make_bellande_step_request(node0, node1, opt.limit, opt.dimensions).await { + Ok(result) => { + println!("{}", serde_json::to_string_pretty(&result)?); + } + Err(e) => { + eprintln!("Error: {}", e); + process::exit(1); + } + } + } + + Ok(()) +}