- Modular builders for `gcc`, `clang`, `msvc`
- Platform-specific binary path generation (Linux/macOS/Windows)

Changed:
- Build configuration moved to `[build]` (`language`, `standard`, `compiler`)
- `dcr.toml` formatting now includes the `[build]` section
- Build uses built-in `debug/release` flags per compiler
- Build compiles all `*.c/*.cpp` files in `src/` into a single binary (no incremental build)
- Updated `dcr.toml` examples in documentation
This commit is contained in:
2026-02-21 16:12:20 +08:00
parent e87e0cbc47
commit 0f48151b68
17 changed files with 544 additions and 98 deletions

View File

@@ -1,5 +1,17 @@
# Changelog
## [0.2.3] - 2026-02-21
Added:
- Modular builders for `gcc`, `clang`, `msvc`
- Platform-specific binary path generation (Linux/macOS/Windows)
Changed:
- Build configuration moved to `[build]` (`language`, `standard`, `compiler`)
- `dcr.toml` formatting now includes the `[build]` section
- Build uses built-in `debug/release` flags per compiler
- Build compiles all `*.c/*.cpp` files in `src/` into a single binary (no incremental build)
- Updated `dcr.toml` examples in documentation
## [0.2.2] - 2026-02-20
Changed:
- Reworked `dcr.toml` handling: added read, validation, and edit through `core::config`
@@ -14,40 +26,40 @@ Changed:
- Translated installer script messages in `install.sh` and `install.ps1` to English
## [0.2.0] - 2026-02-17
Изменено:
- Проект переведен с Python на Rust
- CLI и команды (`new`, `init`, `build`, `run`, `clean`, `--help`, `--version`, `--update`) перенесены на Rust
- Обновлен флаг `--update` - добавлена поддержка GNU\Linux, Windows, MacOS
- Обновлены `README.md`, `CONTRIBUTING.md`, `install.sh` под Rust-реализацию
- Добавлен `install.ps1` для Windows
- Обновлен `install.sh` для GNU/Linux и MacOS
Changed:
- Project migrated from Python to Rust
- CLI and commands (`new`, `init`, `build`, `run`, `clean`, `--help`, `--version`, `--update`) ported to Rust
- Updated `--update` flag: added support for GNU/Linux, Windows, macOS
- Updated `README.md`, `CONTRIBUTING.md`, and `install.sh` for the Rust implementation
- Added `install.ps1` for Windows
- Updated `install.sh` for GNU/Linux and macOS
Добавлено:
- Добавлен `install.ps1` для Windows
- Поддержка GNU\Linux, Windows, MacOS(x86_64/arm)
Added:
- Added `install.ps1` for Windows
- Support for GNU/Linux, Windows, macOS (x86_64/arm)
ВАЖНО:
- Код перенесен с помощью нейронных сетей, в будущих версиях будет баг-фикс и изменение логики
Important:
- Code was ported with neural networks; future versions will include bug fixes and logic changes
## [0.1.2] - 2026-02-12
Добавлено:
- Команда обновления
- Скрипт установки `install.sh` и инструкции по установке в README
- Флаг `--version` для вывода версии
Added:
- Update command
- `install.sh` install script and README instructions
- `--version` flag
Изменено:
- Улучшен консольный вывод команд, добавлены цвета и обновлена справка `--help`
- Обновлен запуск проекта и обработка сборки в `run.py`/`build.py`
Changed:
- Improved CLI output, added colors, and updated `--help`
- Updated project run/build handling in `run.py`/`build.py`
## [0.1.1] - 2026-02-11
Изменено:
- Обновлена справка `--help`
- `main.py` теперь корректно запускается при прямом выполнении
Changed:
- Updated `--help`
- `main.py` now runs correctly when executed directly
## [0.1.0] - 2026-02-11
Первый публичный релиз.
First public release.
Добавлено:
- Базовые команды `new`, `init`, `build`, `run`, `clean`
- Профили сборки `debug` и `release`
- Шаблоны `dcr.toml` и `src/main.c`
Added:
- Base commands `new`, `init`, `build`, `run`, `clean`
- Build profiles `debug` and `release`
- `dcr.toml` and `src/main.c` templates

2
Cargo.lock generated
View File

@@ -62,7 +62,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "dcr"
version = "0.2.2"
version = "0.2.3"
dependencies = [
"reqwest",
"self-replace",

View File

@@ -1,6 +1,6 @@
[package]
name = "dcr"
version = "0.2.2"
version = "0.2.3"
edition = "2024"
build = "build.rs"

View File

@@ -107,15 +107,15 @@ Builds the project. If no profile is specified, `--debug` is used.
Builds the project and runs the binary. If no profile is specified, `--debug` is used.
Run manually:
`./target/<profile>/main`
`./target/<profile>/<name>`
### `dcr clean`
Removes the `target` directory in the project root.
## Build Profiles
Two profiles are supported:
- `--debug` (default) - flags: `-O0 -g -Wall -Wextra -fno-omit-frame-pointer -DDEBUG`
- `--release` - flags: `-O3 -DNDEBUG -march=native`
- `--debug` (default) - flags: `-O0 -g -Wall -Wextra -fno-omit-frame-pointer -DDEBUG`(Clang)
- `--release` - flags: `-O3 -DNDEBUG -march=native`(Clang)
## Configuration
The main project file is `dcr.toml`.
@@ -126,7 +126,10 @@ Example `dcr.toml`:
[package]
name = "hello"
version = "0.1.0"
[build]
language = "c"
standard = "c11"
compiler = "clang"
[dependencies]
@@ -134,7 +137,7 @@ compiler = "clang"
## Requirements
- Rust toolchain (`rustc`, `cargo`) - for building DCR from source
- C compiler (`clang`, `gcc`, or others)
- C compiler (`clang`, `gcc`, or 'cl'(msvc))
## Releases
Releases are built automatically via GitHub Actions (`.github/workflows/release.yml`) when a tag matching `v*` is pushed.

View File

@@ -107,15 +107,15 @@ hello/
Собирает проект и запускает бинарник. Если профиль не указан, используется `--debug`.
Запуск вручную:
`./target/<profile>/main`
`./target/<profile>/<name>`
### `dcr clean`
Удаляет папку `target` в корне проекта.
## Профили сборки
Поддерживаются два профиля:
- `--debug` (по умолчанию) — флаги: `-O0 -g -Wall -Wextra -fno-omit-frame-pointer -DDEBUG`
- `--release` — флаги: `-O3 -DNDEBUG -march=native`
- `--debug` (по умолчанию) — флаги: `-O0 -g -Wall -Wextra -fno-omit-frame-pointer -DDEBUG`(Clang)
- `--release` — флаги: `-O3 -DNDEBUG -march=native`(Clang)
## Конфигурация
Основной файл проекта — `dcr.toml`.
@@ -126,7 +126,10 @@ hello/
[package]
name = "hello"
version = "0.1.0"
[build]
language = "c"
standard = "c11"
compiler = "clang"
[dependencies]
@@ -134,7 +137,7 @@ compiler = "clang"
## Требования
- Rust toolchain (`rustc`, `cargo`) - Для ручной сборки DCR
- C-компилятор (`clang`, `gcc` или другие)
- C-компилятор (`clang`, `gcc` или 'cl'(msvc))
## Релизы
Релизы собираются автоматически через GitHub Actions (`.github/workflows/release.yml`) при пуше тега формата `v*`.

View File

@@ -1,15 +1,13 @@
use crate::config::{PROFILE, flags};
use crate::core::builder::{BuildContext, build as build_project};
use crate::core::config::Config;
use crate::utils::fs::check_dir;
use crate::utils::log::{error, warn};
use crate::utils::text::{BOLD_GREEN, colored};
use std::fs;
use std::process::Command;
use std::time::Instant;
pub fn build(args: &[String]) -> i32 {
let mut active_profile = PROFILE.to_string();
let items = check_dir(None).unwrap_or_default();
if !items.contains(&"dcr.toml".to_string()) {
error("dcr.toml file not found");
@@ -23,73 +21,92 @@ pub fn build(args: &[String]) -> i32 {
return 1;
}
};
let project_compiler: &str = config
.get("package.compiler")
.and_then(|v| v.as_str())
.unwrap_or("");
let project_name: &str = config
.get("package.name")
.and_then(|v| v.as_str())
.unwrap_or("");
if let Some(first_arg) = args.first() {
if first_arg.starts_with("--") {
let candidate = first_arg.trim_start_matches("--");
if flags(candidate).is_some() {
active_profile = candidate.to_string();
} else {
warn("Unknown build flag");
return 1;
}
} else {
warn("Unknown argument");
return 1;
}
}
let active_profile = match parse_profile(args) {
Ok(profile) => profile,
Err(code) => return code,
};
let project_name = get_config_str(&config, "package.name");
let project_compiler = get_config_str(&config, "build.compiler");
let build_language = get_config_str(&config, "build.language");
let build_standard = get_config_str(&config, "build.standard");
println!(
" Building project `{}`\n Profile: {}\n Compiler: {}\n",
colored(project_name, BOLD_GREEN),
colored(&project_name, BOLD_GREEN),
colored(&active_profile, BOLD_GREEN),
colored(project_compiler, BOLD_GREEN)
colored(&project_compiler, BOLD_GREEN)
);
if !items.contains(&"target".to_string()) {
let _ = fs::create_dir("./target");
}
let target_items = check_dir(Some("target")).unwrap_or_default();
if !target_items.contains(&active_profile) {
let _ = fs::create_dir(format!("./target/{active_profile}"));
}
ensure_target_dirs(&items, &active_profile);
let src_items = check_dir(Some("src")).unwrap_or_default();
if src_items.contains(&"main.c".to_string()) {
let compile_flags = flags(&active_profile).unwrap_or(&[]);
let start_time = Instant::now();
let mut cmd = Command::new(project_compiler);
cmd.arg("./src/main.c");
for flag in compile_flags {
cmd.arg(flag);
}
cmd.arg("-o")
.arg(format!("./target/{active_profile}/{project_name}"));
match cmd.status() {
Ok(status) if status.success() => {
let times = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
let ctx = BuildContext {
profile: &active_profile,
project_name: &project_name,
compiler: &project_compiler,
language: &build_language,
standard: &build_standard,
};
match run_build(&ctx) {
Ok(times) => {
println!(
" {} Build completed successfully in {} seconds",
colored("✔", BOLD_GREEN),
colored(&times.to_string(), BOLD_GREEN)
);
return 0;
0
}
_ => {
error("Build failed");
return 1;
Err(msg) => {
error(&msg);
1
}
}
}
0
fn parse_profile(args: &[String]) -> Result<String, i32> {
if let Some(first_arg) = args.first() {
if first_arg.starts_with("--") {
let candidate = first_arg.trim_start_matches("--");
if flags(candidate).is_some() {
return Ok(candidate.to_string());
}
warn("Unknown build flag");
return Err(1);
}
warn("Unknown argument");
return Err(1);
}
Ok(PROFILE.to_string())
}
fn get_config_str(config: &Config, key: &str) -> String {
config
.get(key)
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string()
}
fn ensure_target_dirs(items: &[String], profile: &str) {
if !items.contains(&"target".to_string()) {
let _ = fs::create_dir("./target");
}
let target_items = check_dir(Some("target")).unwrap_or_default();
if !target_items.contains(&profile.to_string()) {
let _ = fs::create_dir(format!("./target/{profile}"));
}
}
fn run_build(ctx: &BuildContext) -> Result<f64, String> {
let start_time = Instant::now();
match build_project(ctx) {
Ok(times) => {
let times = if times == 0.0 {
((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0
} else {
times
};
Ok(times)
}
Err(_) => Err("Build failed".to_string()),
}
}

81
src/core/builder/clang.rs Normal file
View File

@@ -0,0 +1,81 @@
use crate::core::builder::BuildContext;
use crate::platform;
use std::fs;
use std::process::Command;
use std::time::Instant;
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
let compiler = if ctx.compiler.is_empty() {
"cc"
} else {
ctx.compiler
};
let start_time = Instant::now();
let mut cmd = Command::new(compiler);
let sources = collect_sources(ctx.language)?;
for source in &sources {
cmd.arg(source);
}
if !ctx.standard.is_empty() {
cmd.arg(format!("-std={}", ctx.standard));
}
for flag in default_flags(ctx.profile) {
cmd.arg(flag);
}
cmd.arg("-o")
.arg(platform::bin_path(ctx.profile, ctx.project_name));
match cmd.status() {
Ok(status) if status.success() => {
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
Ok(elapsed)
}
Ok(_) => Err("Build failed".to_string()),
Err(err) => Err(format!("Build failed: {err}")),
}
}
fn collect_sources(language: &str) -> Result<Vec<String>, String> {
let lang = language.to_lowercase();
let mut sources = Vec::new();
let entries = fs::read_dir("./src").map_err(|err| format!("src dir error: {err}"))?;
for entry in entries {
let entry = entry.map_err(|err| format!("src dir error: {err}"))?;
let path = entry.path();
if !path.is_file() {
continue;
}
let ext = path
.extension()
.and_then(|v| v.to_str())
.unwrap_or("")
.to_lowercase();
let file = path.to_string_lossy().to_string();
let allowed = (lang == "c" && ext == "c")
|| ((lang == "c++" || lang == "cpp" || lang == "cxx")
&& (ext == "cpp" || ext == "cxx" || ext == "cc"));
if allowed {
sources.push(file);
}
}
sources.sort();
if sources.is_empty() {
return Err("No source files found in ./src".to_string());
}
Ok(sources)
}
fn default_flags(profile: &str) -> &'static [&'static str] {
match profile {
"release" => &["-O3", "-DNDEBUG", "-march=native"],
"debug" => &[
"-O0",
"-g",
"-Wall",
"-Wextra",
"-fno-omit-frame-pointer",
"-DDEBUG",
],
_ => &[],
}
}

81
src/core/builder/gcc.rs Normal file
View File

@@ -0,0 +1,81 @@
use crate::core::builder::BuildContext;
use crate::platform;
use std::fs;
use std::process::Command;
use std::time::Instant;
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
let compiler = if ctx.compiler.is_empty() {
"cc"
} else {
ctx.compiler
};
let start_time = Instant::now();
let mut cmd = Command::new(compiler);
let sources = collect_sources(ctx.language)?;
for source in &sources {
cmd.arg(source);
}
if !ctx.standard.is_empty() {
cmd.arg(format!("-std={}", ctx.standard));
}
for flag in default_flags(ctx.profile) {
cmd.arg(flag);
}
cmd.arg("-o")
.arg(platform::bin_path(ctx.profile, ctx.project_name));
match cmd.status() {
Ok(status) if status.success() => {
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
Ok(elapsed)
}
Ok(_) => Err("Build failed".to_string()),
Err(err) => Err(format!("Build failed: {err}")),
}
}
fn collect_sources(language: &str) -> Result<Vec<String>, String> {
let lang = language.to_lowercase();
let mut sources = Vec::new();
let entries = fs::read_dir("./src").map_err(|err| format!("src dir error: {err}"))?;
for entry in entries {
let entry = entry.map_err(|err| format!("src dir error: {err}"))?;
let path = entry.path();
if !path.is_file() {
continue;
}
let ext = path
.extension()
.and_then(|v| v.to_str())
.unwrap_or("")
.to_lowercase();
let file = path.to_string_lossy().to_string();
let allowed = (lang == "c" && ext == "c")
|| ((lang == "c++" || lang == "cpp" || lang == "cxx")
&& (ext == "cpp" || ext == "cxx" || ext == "cc"));
if allowed {
sources.push(file);
}
}
sources.sort();
if sources.is_empty() {
return Err("No source files found in ./src".to_string());
}
Ok(sources)
}
fn default_flags(profile: &str) -> &'static [&'static str] {
match profile {
"release" => &["-O3", "-DNDEBUG", "-march=native"],
"debug" => &[
"-O0",
"-g",
"-Wall",
"-Wextra",
"-fno-omit-frame-pointer",
"-DDEBUG",
],
_ => &[],
}
}

28
src/core/builder/mod.rs Normal file
View File

@@ -0,0 +1,28 @@
pub mod clang;
pub mod gcc;
pub mod msvc;
pub struct BuildContext<'a> {
pub profile: &'a str,
pub project_name: &'a str,
pub compiler: &'a str,
pub language: &'a str,
pub standard: &'a str,
}
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
let compiler = ctx.compiler.to_lowercase();
if compiler.contains("clang-cl") {
return msvc::build(ctx);
}
if compiler.contains("gcc") || compiler.contains("g++") {
return gcc::build(ctx);
}
if compiler.contains("clang") || compiler.contains("clang++") {
return clang::build(ctx);
}
if compiler == "cl" || compiler.contains("msvc") {
return msvc::build(ctx);
}
clang::build(ctx)
}

116
src/core/builder/msvc.rs Normal file
View File

@@ -0,0 +1,116 @@
use crate::core::builder::BuildContext;
use crate::platform;
use std::fs;
use std::process::Command;
use std::time::Instant;
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
let compiler = if ctx.compiler.is_empty() {
"cl"
} else {
ctx.compiler
};
let start_time = Instant::now();
let mut cmd = Command::new(compiler);
let sources = collect_sources(ctx.language)?;
cmd.arg("/nologo");
match ctx.language.to_lowercase().as_str() {
"c" => {
cmd.arg("/TC");
}
"c++" | "cpp" | "cxx" => {
cmd.arg("/TP");
}
_ => {
return Err("Unsupported language".to_string());
}
}
if !ctx.standard.is_empty() {
let std_flag = msvc_standard_flag(ctx.language, ctx.standard)?;
cmd.arg(std_flag);
}
for flag in default_flags(ctx.profile) {
cmd.arg(flag);
}
for source in &sources {
cmd.arg(source);
}
cmd.arg(format!(
"/Fe:{}",
platform::bin_path(ctx.profile, ctx.project_name)
));
match cmd.status() {
Ok(status) if status.success() => {
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
Ok(elapsed)
}
Ok(_) => Err("Build failed".to_string()),
Err(err) => Err(format!("Build failed: {err}")),
}
}
fn collect_sources(language: &str) -> Result<Vec<String>, String> {
let lang = language.to_lowercase();
let mut sources = Vec::new();
let entries = fs::read_dir("./src").map_err(|err| format!("src dir error: {err}"))?;
for entry in entries {
let entry = entry.map_err(|err| format!("src dir error: {err}"))?;
let path = entry.path();
if !path.is_file() {
continue;
}
let ext = path
.extension()
.and_then(|v| v.to_str())
.unwrap_or("")
.to_lowercase();
let file = path.to_string_lossy().to_string();
let allowed = (lang == "c" && ext == "c")
|| ((lang == "c++" || lang == "cpp" || lang == "cxx")
&& (ext == "cpp" || ext == "cxx" || ext == "cc"));
if allowed {
sources.push(file);
}
}
sources.sort();
if sources.is_empty() {
return Err("No source files found in ./src".to_string());
}
Ok(sources)
}
fn msvc_standard_flag(language: &str, standard: &str) -> Result<String, String> {
let lang = language.to_lowercase();
let std = standard.to_lowercase();
if lang == "c" {
return match std.as_str() {
"c11" => Ok("/std:c11".to_string()),
"c17" => Ok("/std:c17".to_string()),
_ => Err("Unsupported C standard for MSVC".to_string()),
};
}
if lang == "c++" || lang == "cpp" || lang == "cxx" {
return match std.as_str() {
"c++11" => Ok("/std:c++11".to_string()),
"c++14" => Ok("/std:c++14".to_string()),
"c++17" => Ok("/std:c++17".to_string()),
"c++20" => Ok("/std:c++20".to_string()),
"c++23" => Ok("/std:c++latest".to_string()),
_ => Err("Unsupported C++ standard for MSVC".to_string()),
};
}
Err("Unsupported language".to_string())
}
fn default_flags(profile: &str) -> &'static [&'static str] {
match profile {
"release" => &["/O2", "/DNDEBUG"],
"debug" => &["/Od", "/Zi", "/W4", "/DDEBUG", "/Oy-"],
_ => &[],
}
}

View File

@@ -5,6 +5,7 @@ use toml::map::Map;
const DEFAULT_VERSION: &str = "0.1.0";
const DEFAULT_LANGUAGE: &str = "c";
const DEFAULT_STANDARD: &str = "c11";
const DEFAULT_COMPILER: &str = "clang";
#[derive(Debug)]
@@ -101,12 +102,24 @@ impl Config {
.and_then(|v| v.as_table())
.ok_or_else(|| ConfigError::Invalid("missing [package]".into()))?;
for key in ["name", "version", "language", "compiler"] {
for key in ["name", "version"] {
let value = package.get(key).and_then(|v| v.as_str()).unwrap_or("");
if value.trim().is_empty() {
return Err(ConfigError::Invalid(format!("package.{key} is empty")));
}
}
let build = self
.get("build")
.and_then(|v| v.as_table())
.ok_or_else(|| ConfigError::Invalid("missing [build]".into()))?;
for key in ["language", "standard", "compiler"] {
let value = build.get(key).and_then(|v| v.as_str()).unwrap_or("");
if value.trim().is_empty() {
return Err(ConfigError::Invalid(format!("build.{key} is empty")));
}
}
Ok(())
}
@@ -128,7 +141,7 @@ fn read_toml(path: &Path) -> Result<Value, ConfigError> {
}
fn write_toml(path: &Path, value: &Value) -> Result<(), ConfigError> {
let content = toml::to_string(value)?;
let content = format_toml(value)?;
fs::write(path, content)?;
Ok(())
}
@@ -145,22 +158,78 @@ fn default_toml() -> Result<Value, ConfigError> {
"version".to_string(),
Value::String(DEFAULT_VERSION.to_string()),
);
package.insert(
let mut build = Map::new();
build.insert(
"language".to_string(),
Value::String(DEFAULT_LANGUAGE.to_string()),
);
package.insert(
build.insert(
"standard".to_string(),
Value::String(DEFAULT_STANDARD.to_string()),
);
build.insert(
"compiler".to_string(),
Value::String(DEFAULT_COMPILER.to_string()),
);
let mut root = Map::new();
root.insert("package".to_string(), Value::Table(package));
root.insert("build".to_string(), Value::Table(build));
root.insert("dependencies".to_string(), Value::Table(Map::new()));
Ok(Value::Table(root))
}
fn format_toml(value: &Value) -> Result<String, ConfigError> {
let root = value
.as_table()
.ok_or_else(|| ConfigError::Invalid("root is not a table".into()))?;
let package = root
.get("package")
.and_then(|v| v.as_table())
.ok_or_else(|| ConfigError::Invalid("missing [package]".into()))?;
let build = root
.get("build")
.and_then(|v| v.as_table())
.ok_or_else(|| ConfigError::Invalid("missing [build]".into()))?;
let deps = root
.get("dependencies")
.and_then(|v| v.as_table())
.ok_or_else(|| ConfigError::Invalid("missing [dependencies]".into()))?;
let name = package.get("name").and_then(|v| v.as_str()).unwrap_or("");
let version = package
.get("version")
.and_then(|v| v.as_str())
.unwrap_or("");
let language = build.get("language").and_then(|v| v.as_str()).unwrap_or("");
let standard = build.get("standard").and_then(|v| v.as_str()).unwrap_or("");
let compiler = build.get("compiler").and_then(|v| v.as_str()).unwrap_or("");
let mut out = String::new();
out.push_str("[package]\n");
out.push_str(&format!("name = \"{name}\"\n"));
out.push_str(&format!("version = \"{version}\"\n\n"));
out.push_str("[build]\n");
out.push_str(&format!("language = \"{language}\"\n"));
out.push_str(&format!("standard = \"{standard}\"\n"));
out.push_str(&format!("compiler = \"{compiler}\"\n\n"));
out.push_str("[dependencies]\n");
if !deps.is_empty() {
let mut keys: Vec<&String> = deps.keys().collect();
keys.sort();
for key in keys {
let val = deps.get(key).and_then(|v| v.as_str()).unwrap_or("");
out.push_str(&format!("{key} = \"{val}\"\n"));
}
}
Ok(out)
}
fn get_path<'a>(value: &'a Value, path: &[&str]) -> Option<&'a Value> {
let mut current = value;
for key in path {

View File

@@ -1 +1,2 @@
pub mod builder;
pub mod config;

View File

@@ -2,6 +2,7 @@ use crate::utils::log::error;
mod cli;
mod config;
mod core;
mod platform;
mod utils;
fn main() {

3
src/platform/linux.rs Normal file
View File

@@ -0,0 +1,3 @@
pub fn bin_path(profile: &str, name: &str) -> String {
format!("./target/{profile}/{name}")
}

3
src/platform/macos.rs Normal file
View File

@@ -0,0 +1,3 @@
pub fn bin_path(profile: &str, name: &str) -> String {
format!("./target/{profile}/{name}")
}

25
src/platform/mod.rs Normal file
View File

@@ -0,0 +1,25 @@
#[cfg(target_os = "linux")]
pub mod linux;
#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(target_os = "windows")]
pub mod windows;
pub fn bin_path(profile: &str, name: &str) -> String {
#[cfg(target_os = "linux")]
{
linux::bin_path(profile, name)
}
#[cfg(target_os = "macos")]
{
return macos::bin_path(profile, name);
}
#[cfg(target_os = "windows")]
{
return windows::bin_path(profile, name);
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
{
format!("./target/{profile}/{name}")
}
}

3
src/platform/windows.rs Normal file
View File

@@ -0,0 +1,3 @@
pub fn bin_path(profile: &str, name: &str) -> String {
format!("./target/{profile}/{name}.exe")
}