12 Commits

Author SHA1 Message Date
a890f7e6eb Merge pull request #42 from alex-xr555/cleaned_cli_styles
refactor: Changing the style system
2026-09-25 23:29:39 +08:00
alex-xr555
3b6251ec33 refactor: the style system has been changed and replaced functions (printc, colored) have been removed in src/utils/cli_styles.rs. Most files in src/cli have been heavily modified to migrate to the new system 2026-09-24 01:10:19 +03:00
alex-xr555
e9d09eacaf refactor: created new macro sprintln! and sprint! (src/utils/log.rs) for print stylized text to the console. An additional comment has been created, and access to owo_colors via pub use in src/utils/cli_styles.rs 2026-09-24 00:36:51 +03:00
alex-xr555
2f4cde9177 refactor: renamed src/utils/text.rs to cli_styles 2026-09-24 00:20:43 +03:00
552f6c8e4f Merge pull request #41 from alex-xr555/refactor
refactor: Simplify use blocks
2026-09-20 18:28:22 +08:00
alex-xr555
da84916e10 Simplified use blocks 2026-09-19 18:11:28 +03:00
cb6ae3dcc7 Merge pull request #40 from alex-xr555/log 2026-09-19 09:28:18 +08:00
alex-xr555
10123ddedb Translation of comments into English, minor changes for completeness. 2026-09-15 18:34:54 +03:00
alex-xr555
0a140c75d8 Clarified the use block 2026-09-15 18:34:54 +03:00
alex-xr555
aa1d4ba807 The logging system has been brought to a fully functional level. 2026-09-15 18:34:54 +03:00
alex-xr555
991d1c20af Absolutely all logs have been replaced with the new system on log. 2026-09-15 18:34:54 +03:00
alex-xr555
df674defe1 Start of the logging system replacement 2026-09-15 18:34:54 +03:00
41 changed files with 745 additions and 456 deletions

285
Cargo.lock generated
View File

@@ -2,6 +2,15 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "aho-corasick"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@@ -11,6 +20,56 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.102" version = "1.0.102"
@@ -106,6 +165,12 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4" version = "0.9.4"
@@ -157,8 +222,11 @@ name = "dcr"
version = "0.9.1" version = "0.9.1"
dependencies = [ dependencies = [
"ctrlc", "ctrlc",
"env_logger",
"fatfs", "fatfs",
"glob", "glob",
"log",
"owo-colors",
"self-replace", "self-replace",
"serde", "serde",
"serde_json", "serde_json",
@@ -168,6 +236,37 @@ dependencies = [
"ureq", "ureq",
] ]
[[package]]
name = "defmt"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1"
dependencies = [
"bitflags 1.3.2",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8"
dependencies = [
"defmt-parser",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@@ -198,7 +297,30 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
]
[[package]]
name = "env_filter"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"jiff",
"log",
] ]
[[package]] [[package]]
@@ -475,12 +597,54 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.18" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jiff"
version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc"
dependencies = [
"defmt",
"jiff-core",
"jiff-static",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
]
[[package]]
name = "jiff-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09"
dependencies = [
"defmt",
]
[[package]]
name = "jiff-static"
version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204"
dependencies = [
"jiff-core",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.103" version = "0.3.103"
@@ -518,9 +682,9 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.30" version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]] [[package]]
name = "memchr" name = "memchr"
@@ -570,12 +734,24 @@ version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]] [[package]]
name = "openssl-probe" name = "openssl-probe"
version = "0.1.6" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "owo-colors"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.2" version = "2.3.2"
@@ -588,6 +764,21 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "portable-atomic"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
[[package]]
name = "portable-atomic-util"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715"
dependencies = [
"portable-atomic",
]
[[package]] [[package]]
name = "potential_utf" name = "potential_utf"
version = "0.1.5" version = "0.1.5"
@@ -604,7 +795,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn", "syn 2.0.117",
] ]
[[package]] [[package]]
@@ -631,6 +822,35 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "regex"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.1.4" version = "1.1.4"
@@ -757,7 +977,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
] ]
[[package]] [[package]]
@@ -828,6 +1048,17 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "syn"
version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "synstructure" name = "synstructure"
version = "0.13.2" version = "0.13.2"
@@ -836,7 +1067,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
] ]
[[package]] [[package]]
@@ -852,6 +1083,26 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "thiserror"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.5",
]
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.8.3" version = "0.8.3"
@@ -976,6 +1227,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@@ -1032,7 +1289,7 @@ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@@ -1100,7 +1357,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
] ]
[[package]] [[package]]
@@ -1111,7 +1368,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
] ]
[[package]] [[package]]
@@ -1271,7 +1528,7 @@ dependencies = [
"heck", "heck",
"indexmap", "indexmap",
"prettyplease", "prettyplease",
"syn", "syn 2.0.117",
"wasm-metadata", "wasm-metadata",
"wit-bindgen-core", "wit-bindgen-core",
"wit-component", "wit-component",
@@ -1287,7 +1544,7 @@ dependencies = [
"prettyplease", "prettyplease",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
"wit-bindgen-core", "wit-bindgen-core",
"wit-bindgen-rust", "wit-bindgen-rust",
] ]
@@ -1354,7 +1611,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
"synstructure", "synstructure",
] ]
@@ -1375,7 +1632,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
"synstructure", "synstructure",
] ]
@@ -1415,7 +1672,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.117",
] ]
[[package]] [[package]]

View File

@@ -75,6 +75,11 @@ serde_json = "1"
ctrlc = "3" ctrlc = "3"
fatfs = { version = "0.3", optional = true } fatfs = { version = "0.3", optional = true }
# Logging
log = "0.4.34"
env_logger = "0.11.11"
owo-colors = "4"
[profile.release] [profile.release]
opt-level = "z" opt-level = "z"
lto = "thin" lto = "thin"

View File

@@ -15,13 +15,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::core::build_config::Config; use crate::prelude::*;
use crate::core::deps::register;
use crate::core::{build_config::Config, deps::register};
use crate::utils::cli_styles::{Colorize, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST, SUCCESS_ST};
use crate::utils::fs::find_project_root; use crate::utils::fs::find_project_root;
use crate::utils::log::error; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc}; use toml::{Value, map::Map};
use toml::Value;
use toml::map::Map;
/// Parsed arguments for the `dcr add` command. /// Parsed arguments for the `dcr add` command.
/// ///
@@ -59,16 +59,16 @@ pub struct AddArgs {
/// Process exit code: `0` on success, non-zero on usage or I/O errors. /// Process exit code: `0` on success, non-zero on usage or I/O errors.
pub fn add(args: &[String]) -> i32 { pub fn add(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc( sprintln!(
" dcr add <name> <source> [--branch <b>] [--tag <t>] [--rev <r>]", HELP_EXAMPLES_ST,
BOLD_CYAN, " dcr add <name> <source> [--branch <b>] [--tag <t>] [--rev <r>]"
); );
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Adds a dependency to the project."); println!(" Adds a dependency to the project.");
println!(); println!();
printc("SOURCES:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "SOURCES:");
println!(" github:user/repo GitHub repository"); println!(" github:user/repo GitHub repository");
println!(" gitlab:user/repo GitLab repository"); println!(" gitlab:user/repo GitLab repository");
println!(" git:host.com/user/repo Generic git repository"); println!(" git:host.com/user/repo Generic git repository");
@@ -76,7 +76,7 @@ pub fn add(args: &[String]) -> i32 {
println!(" pkg-config:fmt System pkg-config dependency"); println!(" pkg-config:fmt System pkg-config dependency");
println!(" <version> Version from registry"); println!(" <version> Version from registry");
println!(); println!();
printc("OPTIONS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "OPTIONS:");
println!(" --branch <b> Use a specific branch"); println!(" --branch <b> Use a specific branch");
println!(" --tag <t> Use a specific tag"); println!(" --tag <t> Use a specific tag");
println!(" --rev <r> Use a specific commit hash"); println!(" --rev <r> Use a specific commit hash");
@@ -91,7 +91,7 @@ pub fn add(args: &[String]) -> i32 {
let start_dir = match std::env::current_dir() { let start_dir = match std::env::current_dir() {
Ok(dir) => dir, Ok(dir) => dir,
Err(_) => { Err(_) => {
error("Failed to determine current directory"); error!("Failed to determine current directory");
return 1; return 1;
} }
}; };
@@ -99,11 +99,11 @@ pub fn add(args: &[String]) -> i32 {
let root = match find_project_root(&start_dir) { let root = match find_project_root(&start_dir) {
Ok(Some(dir)) => dir, Ok(Some(dir)) => dir,
Ok(None) => { Ok(None) => {
error("dcr.toml file not found"); error!("dcr.toml file not found");
return 1; return 1;
} }
Err(_) => { Err(_) => {
error("Failed to find project root"); error!("Failed to find project root");
return 1; return 1;
} }
}; };
@@ -111,7 +111,7 @@ pub fn add(args: &[String]) -> i32 {
let mut config = match Config::open(&root.join("dcr.toml").to_string_lossy()) { let mut config = match Config::open(&root.join("dcr.toml").to_string_lossy()) {
Ok(cfg) => cfg, Ok(cfg) => cfg,
Err(err) => { Err(err) => {
error(&err.to_string()); error!("{err}");
return 1; return 1;
} }
}; };
@@ -154,19 +154,19 @@ pub fn add(args: &[String]) -> i32 {
} else if let Some(version) = add_args.version_from_registry { } else if let Some(version) = add_args.version_from_registry {
Value::String(version) Value::String(version)
} else { } else {
error("Dependency source (path, git, or pkg-config) must be provided"); error!("Dependency source (path, git, or pkg-config) must be provided");
return 1; return 1;
}; };
let key = format!("dependencies.{}", add_args.name); let key = format!("dependencies.{}", add_args.name);
if let Err(err) = config.edit(&key, dep_value) { if let Err(err) = config.edit(&key, dep_value) {
error(&format!("Failed to update dcr.toml: {}", err)); error!("Failed to update dcr.toml: {err}");
return 1; return 1;
} }
println!( println!(
" {} dependency `{}` to dcr.toml", " {} dependency `{}` to dcr.toml",
colored("Added", BOLD_GREEN), "Added".style(SUCCESS_ST),
add_args.name add_args.name
); );
0 0
@@ -179,13 +179,13 @@ pub fn add(args: &[String]) -> i32 {
/// `path:`, `github:`, `gitlab:`, or `git:` prefixes, or a full git URL. /// `path:`, `github:`, `gitlab:`, or `git:` prefixes, or a full git URL.
fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> { fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
if args.is_empty() { if args.is_empty() {
error("Usage: dcr add <name> <source> [--branch <branch> | --tag <tag> | --rev <rev>]"); error!("Usage: dcr add <name> <source> [--branch <branch> | --tag <tag> | --rev <rev>]");
error("Sources:"); error!("Sources:");
error(" github:user/repo -> github.com/user/repo"); error!(" github:user/repo -> github.com/user/repo");
error(" gitlab:user/repo -> gitlab.com/user/repo"); error!(" gitlab:user/repo -> gitlab.com/user/repo");
error(" git:host.com/user/repo -> host.com/user/repo"); error!(" git:host.com/user/repo -> host.com/user/repo");
error(" path:./path/to/lib -> local path"); error!(" path:./path/to/lib -> local path");
error(" pkg-config:fmt -> system pkg-config package"); error!(" pkg-config:fmt -> system pkg-config package");
return Err(1); return Err(1);
} }
@@ -198,7 +198,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
let mut iter = args.iter(); let mut iter = args.iter();
if let Some(n) = iter.next() { if let Some(n) = iter.next() {
if n.starts_with("--") { if n.starts_with("--") {
error("First argument must be the dependency name"); error!("First argument must be the dependency name");
return Err(1); return Err(1);
} }
name = n.clone(); name = n.clone();
@@ -206,7 +206,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
if let Some(s) = iter.next() { if let Some(s) = iter.next() {
if s.starts_with("--") { if s.starts_with("--") {
error("Second argument must be the dependency source"); error!("Second argument must be the dependency source");
return Err(1); return Err(1);
} }
source_spec = s.clone(); source_spec = s.clone();
@@ -225,7 +225,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
) )
}) })
.map_err(|(msg, code)| { .map_err(|(msg, code)| {
error(&msg); error!("{msg}");
code code
})?; })?;
return Ok(AddArgs { return Ok(AddArgs {
@@ -240,7 +240,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
}); });
} }
Err(e) => { Err(e) => {
error(&format!("Cannot add `{}` without source: {}", name, e)); error!("Cannot add `{}` without source: {}", name, e);
return Err(1); return Err(1);
} }
} }
@@ -251,26 +251,26 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
"--branch" => { "--branch" => {
branch = iter.next().cloned(); branch = iter.next().cloned();
if branch.is_none() { if branch.is_none() {
error("--branch requires a value"); error!("--branch requires a value");
return Err(1); return Err(1);
} }
} }
"--tag" => { "--tag" => {
tag = iter.next().cloned(); tag = iter.next().cloned();
if tag.is_none() { if tag.is_none() {
error("--tag requires a value"); error!("--tag requires a value");
return Err(1); return Err(1);
} }
} }
"--rev" => { "--rev" => {
rev = iter.next().cloned(); rev = iter.next().cloned();
if rev.is_none() { if rev.is_none() {
error("--rev requires a value"); error!("--rev requires a value");
return Err(1); return Err(1);
} }
} }
_ => { _ => {
error(&format!("Unknown argument: {}", arg)); error!("Unknown argument: {arg}");
return Err(1); return Err(1);
} }
} }
@@ -292,7 +292,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
.map(str::to_string) .map(str::to_string)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if packages.is_empty() { if packages.is_empty() {
error("pkg-config source must name at least one package"); error!("pkg-config source must name at least one package");
return Err(1); return Err(1);
} }
pkg_config = Some(packages); pkg_config = Some(packages);
@@ -326,7 +326,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
{ {
path = Some(source_spec); path = Some(source_spec);
} else { } else {
error( error!(
"Source must be a path, pkg-config:, a prefixed source (path:, git:, github:, gitlab:), or a full URL", "Source must be a path, pkg-config:, a prefixed source (path:, git:, github:, gitlab:), or a full URL",
); );
return Err(1); return Err(1);

View File

@@ -15,6 +15,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::cli::clean::clean; use crate::cli::clean::clean;
use crate::cli::flags::parse_build_run_flags; use crate::cli::flags::parse_build_run_flags;
use crate::cli::r#gen::{ProjectInfo, write_dcr_metadata}; use crate::cli::r#gen::{ProjectInfo, write_dcr_metadata};
@@ -26,9 +28,11 @@ use crate::utils::build::{
get_config_opt, get_config_str, get_language_with_profile_or_default, get_string_with_profile, get_config_opt, get_config_str, get_language_with_profile_or_default, get_string_with_profile,
normalize_kind, resolve_artifact_target_dir, resolve_compiler, normalize_kind, resolve_artifact_target_dir, resolve_compiler,
}; };
use crate::utils::cli_styles::{
BOLD_CYAN, BOLD_YELLOW, Colorize, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST, SUCCESS_ST, Style,
};
use crate::utils::fs::{canonicalize_path, find_project_root}; use crate::utils::fs::{canonicalize_path, find_project_root};
use crate::utils::log::error; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, BOLD_YELLOW, colored, printc};
use std::io::IsTerminal; use std::io::IsTerminal;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, atomic::AtomicBool}; use std::sync::{Arc, atomic::AtomicBool};
@@ -36,8 +40,8 @@ use std::sync::{Arc, atomic::AtomicBool};
pub use crate::core::build::get_build_string_with_profile; pub use crate::core::build::get_build_string_with_profile;
/// Formats a left-aligned status verb with the given style for CLI output. /// Formats a left-aligned status verb with the given style for CLI output.
fn status(verb: &str, style: &str) -> String { fn status(verb: &str, style: Style) -> String {
colored(&format!("{verb:<9}"), style) format!("{verb:<9}").style(style).to_string()
} }
/// CLI build reporter that prints human-readable progress to stderr/stdout. /// CLI build reporter that prints human-readable progress to stderr/stdout.
@@ -50,7 +54,7 @@ impl CliReporter {
/// - `verb`: Short status label (e.g. `"target"`, `"dep"`). /// - `verb`: Short status label (e.g. `"target"`, `"dep"`).
/// - `style`: Color/style constant for the verb. /// - `style`: Color/style constant for the verb.
/// - `rest`: Remaining message text after the verb. /// - `rest`: Remaining message text after the verb.
fn line(&self, verb: &str, style: &str, rest: &str) { fn line(&self, verb: &str, style: Style, rest: &str) {
eprintln!(" {} {}", status(verb, style), rest); eprintln!(" {} {}", status(verb, style), rest);
} }
} }
@@ -94,12 +98,12 @@ impl BuildReporter for CliReporter {
common::finish_progress_line(); common::finish_progress_line();
// Only announce deps that were actually rebuilt this run. // Only announce deps that were actually rebuilt this run.
if rebuilt { if rebuilt {
self.line("ready", BOLD_GREEN, &format!("{name} v{version}")); self.line("ready", SUCCESS_ST, &format!("{name} v{version}"));
} }
} }
BuildEvent::Compiling { name, version } => { BuildEvent::Compiling { name, version } => {
common::finish_progress_line(); common::finish_progress_line();
let label = format!(" {} {} v{}", status("compile", BOLD_GREEN), name, version); let label = format!(" {} {} v{}", status("compile", SUCCESS_ST), name, version);
// Terminals use an in-place progress line; non-TTY gets a plain log line. // Terminals use an in-place progress line; non-TTY gets a plain log line.
common::set_progress_label(Some(label.clone())); common::set_progress_label(Some(label.clone()));
if !std::io::stderr().is_terminal() { if !std::io::stderr().is_terminal() {
@@ -112,7 +116,7 @@ impl BuildReporter for CliReporter {
} }
BuildEvent::Finished { secs } => { BuildEvent::Finished { secs } => {
common::finish_progress_line(); common::finish_progress_line();
self.line("done", BOLD_GREEN, &format!("in {secs}s")); self.line("done", SUCCESS_ST, &format!("in {secs}s"));
} }
BuildEvent::CompilerOutput { stream, text } => { BuildEvent::CompilerOutput { stream, text } => {
// Pause the progress spinner so compiler text is not overwritten. // Pause the progress spinner so compiler text is not overwritten.
@@ -140,16 +144,16 @@ impl BuildReporter for CliReporter {
/// Process exit code: `0` on success or help, non-zero on failure. /// Process exit code: `0` on success or help, non-zero on failure.
pub fn build(args: &[String]) -> i32 { pub fn build(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc( sprintln!(
" dcr build [--debug | --release] [--target <triple>] [--force] [--clean] [--verbose] [--print-artifact-path]", HELP_EXAMPLES_ST,
BOLD_CYAN, " dcr build [--debug | --release] [--target <triple>] [--force] [--clean] [--verbose] [--print-artifact-path]"
); );
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Compiles the project. Default profile is --debug."); println!(" Compiles the project. Default profile is --debug.");
println!(); println!();
printc("OPTIONS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "OPTIONS:");
println!(" --debug Build with debug profile (default)"); println!(" --debug Build with debug profile (default)");
println!(" --release Build with release profile"); println!(" --release Build with release profile");
println!(" --target <triple> Cross-compile for the given target"); println!(" --target <triple> Cross-compile for the given target");
@@ -173,18 +177,18 @@ pub fn build(args: &[String]) -> i32 {
let start_dir = match std::env::current_dir() { let start_dir = match std::env::current_dir() {
Ok(dir) => dir, Ok(dir) => dir,
Err(_) => { Err(_) => {
error("Failed to determine current directory"); error!("Failed to determine current directory");
return 1; return 1;
} }
}; };
let root = match find_project_root(&start_dir) { let root = match find_project_root(&start_dir) {
Ok(Some(dir)) => canonicalize_path(&dir), Ok(Some(dir)) => canonicalize_path(&dir),
Ok(None) => { Ok(None) => {
error("dcr.toml file not found"); error!("dcr.toml file not found");
return 1; return 1;
} }
Err(_) => { Err(_) => {
error("Failed to find project root"); error!("Failed to find project root");
return 1; return 1;
} }
}; };
@@ -242,7 +246,7 @@ pub fn build(args: &[String]) -> i32 {
} }
Err(err) => { Err(err) => {
common::finish_progress_line(); common::finish_progress_line();
error(&err.message); error!("{}", err.message);
1 1
} }
} }

View File

@@ -15,15 +15,18 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::core::build_config::Config; use crate::core::build_config::Config;
use crate::core::workspace::parse_workspace; use crate::core::workspace::parse_workspace;
use crate::utils::build::{default_profile_flags, default_target_triple, parse_version_info}; use crate::utils::build::{default_profile_flags, default_target_triple, parse_version_info};
use crate::utils::cli_styles::{
BOLD_GREEN, Colorize, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST, SUCCESS_ST,
};
use crate::utils::fs::{check_dir, find_project_root, with_dir}; use crate::utils::fs::{check_dir, find_project_root, with_dir};
use crate::utils::log::{error, warn}; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
use glob::glob; use glob::glob;
use std::fs; use std::{fs, path::Path};
use std::path::Path;
/// Entry point for the `dcr clean` command. /// Entry point for the `dcr clean` command.
/// ///
@@ -36,16 +39,16 @@ use std::path::Path;
/// Process exit code: `0` on success, `1` on failure. /// Process exit code: `0` on success, `1` on failure.
pub fn clean(args: &[String]) -> i32 { pub fn clean(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc( sprintln!(
" dcr clean [--debug | --release] [--target <triple>] [--all]", HELP_EXAMPLES_ST,
BOLD_CYAN, " dcr clean [--debug | --release] [--target <triple>] [--all]"
); );
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Removes build artifacts from the target directory."); println!(" Removes build artifacts from the target directory.");
println!(); println!();
printc("OPTIONS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "OPTIONS:");
println!(" --debug Clean debug artifacts (default)"); println!(" --debug Clean debug artifacts (default)");
println!(" --release Clean release artifacts"); println!(" --release Clean release artifacts");
println!(" --target <triple> Clean artifacts for a specific target"); println!(" --target <triple> Clean artifacts for a specific target");
@@ -56,25 +59,25 @@ pub fn clean(args: &[String]) -> i32 {
let start_dir = match std::env::current_dir() { let start_dir = match std::env::current_dir() {
Ok(dir) => dir, Ok(dir) => dir,
Err(_) => { Err(_) => {
error("Failed to determine current directory"); error!("Failed to determine current directory");
return 1; return 1;
} }
}; };
let root = match find_project_root(&start_dir) { let root = match find_project_root(&start_dir) {
Ok(Some(dir)) => dir, Ok(Some(dir)) => dir,
Ok(None) => { Ok(None) => {
error("dcr.toml file not found"); error!("dcr.toml file not found");
return 1; return 1;
} }
Err(_) => { Err(_) => {
error("Failed to find project root"); error!("Failed to find project root");
return 1; return 1;
} }
}; };
let flags = match parse_clean_flags(args) { let flags = match parse_clean_flags(args) {
Ok(v) => v, Ok(v) => v,
Err(msg) => { Err(msg) => {
error(&msg); error!("{msg}");
return 1; return 1;
} }
}; };
@@ -83,7 +86,7 @@ pub fn clean(args: &[String]) -> i32 {
match with_dir(&root, || clean_from_root(&root, &flags)) { match with_dir(&root, || clean_from_root(&root, &flags)) {
Ok(()) => 0, Ok(()) => 0,
Err(msg) => { Err(msg) => {
error(&msg); error!("{msg}");
1 1
} }
} }
@@ -210,10 +213,7 @@ fn clean_project_at(
if !items.contains(&"dcr.toml".to_string()) { if !items.contains(&"dcr.toml".to_string()) {
return Err("dcr.toml file not found".to_string()); return Err("dcr.toml file not found".to_string());
} }
println!( println!(" Cleaning project `{}`", project_name.style(BOLD_GREEN));
" Cleaning project `{}`",
colored(&project_name, BOLD_GREEN)
);
if let Some(profile) = profile { if let Some(profile) = profile {
let target_dir = if let Some(t) = target { let target_dir = if let Some(t) = target {
format!("target/{t}/{profile}") format!("target/{t}/{profile}")
@@ -229,16 +229,16 @@ fn clean_project_at(
target_items.contains(&profile.to_string()) target_items.contains(&profile.to_string())
}; };
if !dir_exists { if !dir_exists {
warn(&format!("Directory target/{} not found", target_dir)); warn!("Directory target/{target_dir} not found");
} else { } else {
println!(" Profile: {}", colored(profile, BOLD_GREEN)); println!(" Profile: {}", profile.style(BOLD_GREEN));
if let Some(t) = target { if let Some(t) = target {
println!(" Target: {}", colored(t, BOLD_GREEN)); println!(" Target: {}", t.style(BOLD_GREEN));
} }
let _ = fs::remove_dir_all(&target_dir); let _ = fs::remove_dir_all(&target_dir);
println!( println!(
"{} Removed directory {}", "{} Removed directory {}",
colored("\n ✔", BOLD_GREEN), "\n ✔".style(SUCCESS_ST),
target_dir target_dir
); );
} }
@@ -248,12 +248,9 @@ fn clean_project_at(
if items.contains(&"target".to_string()) { if items.contains(&"target".to_string()) {
let _ = fs::remove_dir_all("target"); let _ = fs::remove_dir_all("target");
println!( println!("{} Removed directory target", "\n ✔".style(SUCCESS_ST));
"{} Removed directory target",
colored("\n ✔", BOLD_GREEN)
);
} else { } else {
warn("Directory target not found"); warn!("Directory target not found");
} }
clean_custom_paths(&config, "debug")?; clean_custom_paths(&config, "debug")?;
clean_custom_paths(&config, "release")?; clean_custom_paths(&config, "release")?;

View File

@@ -15,8 +15,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::utils::log::{error, warn}; use crate::prelude::*;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, printc};
use crate::utils::cli_styles::{HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST};
use crate::utils::log::sprintln;
use serde::Deserialize; use serde::Deserialize;
use std::fs; use std::fs;
use std::io::Read; use std::io::Read;
@@ -55,30 +57,30 @@ struct ReleaseAsset {
/// `0` on success / already up to date / pacman-managed refuse; `1` on error. /// `0` on success / already up to date / pacman-managed refuse; `1` on error.
pub fn flag_update(args: &[String]) -> i32 { pub fn flag_update(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr --update", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr --update");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Updates DCR to the latest version from GitHub Releases."); println!(" Updates DCR to the latest version from GitHub Releases.");
return 0; return 0;
} }
if !args.is_empty() { if !args.is_empty() {
warn("Command does not support additional arguments"); warn!("Command does not support additional arguments");
return 1; return 1;
} }
let current_exe = match std::env::current_exe() { let current_exe = match std::env::current_exe() {
Ok(path) => path, Ok(path) => path,
Err(_) => { Err(_) => {
error("Failed to resolve current binary path"); error!("Failed to resolve current binary path");
return 1; return 1;
} }
}; };
// Refuse self-update when the binary is owned by pacman/AUR. // Refuse self-update when the binary is owned by pacman/AUR.
if let Some(package_name) = pacman_owned_package(&current_exe) { if let Some(package_name) = pacman_owned_package(&current_exe) {
warn("This dcr binary is managed by pacman/AUR"); warn!("This dcr binary is managed by pacman/AUR");
println!( println!(
"Update via package manager: yay/paru -Syu {package_name} or sudo pacman -Syu {package_name}" "Update via package manager: yay/paru -Syu {package_name} or sudo pacman -Syu {package_name}"
); );
@@ -91,7 +93,7 @@ pub fn flag_update(args: &[String]) -> i32 {
let release = match fetch_latest_release() { let release = match fetch_latest_release() {
Ok(release) => release, Ok(release) => release,
Err(err) => { Err(err) => {
error(&format!("Failed to check for updates: {err}")); error!("Failed to check for updates: {err}");
return 1; return 1;
} }
}; };
@@ -109,14 +111,14 @@ pub fn flag_update(args: &[String]) -> i32 {
.iter() .iter()
.find(|asset| candidate_names.iter().any(|name| name == &asset.name)) .find(|asset| candidate_names.iter().any(|name| name == &asset.name))
else { else {
error(&format!("Binary for target {target} not found")); error!("Binary for target {target} not found");
return 1; return 1;
}; };
let bytes = match download_asset(&asset.browser_download_url) { let bytes = match download_asset(&asset.browser_download_url) {
Ok(bytes) => bytes, Ok(bytes) => bytes,
Err(err) => { Err(err) => {
error(&format!("Failed to download update: {err}")); error!("Failed to download update: {err}");
return 1; return 1;
} }
}; };
@@ -124,7 +126,7 @@ pub fn flag_update(args: &[String]) -> i32 {
let temp_path = temp_binary_path(&current_exe); let temp_path = temp_binary_path(&current_exe);
if fs::write(&temp_path, &bytes).is_err() { if fs::write(&temp_path, &bytes).is_err() {
error("Failed to write temporary binary"); error!("Failed to write temporary binary");
return 1; return 1;
} }
set_executable_permissions(&temp_path); set_executable_permissions(&temp_path);
@@ -132,7 +134,7 @@ pub fn flag_update(args: &[String]) -> i32 {
// Replace the running executable in place, then clean up the temp file. // Replace the running executable in place, then clean up the temp file.
if self_replace::self_replace(&temp_path).is_err() { if self_replace::self_replace(&temp_path).is_err() {
let _ = fs::remove_file(&temp_path); let _ = fs::remove_file(&temp_path);
error("Failed to replace current binary"); error!("Failed to replace current binary");
return 1; return 1;
} }

View File

@@ -15,8 +15,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::config::{PROFILE, flags}; use crate::config::{PROFILE, flags};
use crate::utils::log::warn;
/// Flags shared by the `build` and `run` CLI commands. /// Flags shared by the `build` and `run` CLI commands.
pub struct BuildRunFlags { pub struct BuildRunFlags {
@@ -82,7 +83,7 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
while let Some(arg) = iter.next() { while let Some(arg) = iter.next() {
// DCR build/run options are long flags only (`--name`). // DCR build/run options are long flags only (`--name`).
if !arg.starts_with("--") { if !arg.starts_with("--") {
warn("Unknown argument"); warn!("Unknown argument");
return Err(1); return Err(1);
} }
let candidate = arg.trim_start_matches("--"); let candidate = arg.trim_start_matches("--");
@@ -106,7 +107,7 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
if let Some(w) = iter.next() { if let Some(w) = iter.next() {
workspace = Some(w.clone()); workspace = Some(w.clone());
} else { } else {
warn("--workspace requires a value"); warn!("--workspace requires a value");
return Err(1); return Err(1);
} }
continue; continue;
@@ -115,7 +116,7 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
if let Some(t) = iter.next() { if let Some(t) = iter.next() {
target = Some(t.clone()); target = Some(t.clone());
} else { } else {
warn("--target requires a value"); warn!("--target requires a value");
return Err(1); return Err(1);
} }
continue; continue;
@@ -123,13 +124,13 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
// Profile names come from config::flags; only one profile may be set. // Profile names come from config::flags; only one profile may be set.
if flags(candidate).is_some() { if flags(candidate).is_some() {
if profile != PROFILE { if profile != PROFILE {
warn("Duplicate profile flag"); warn!("Duplicate profile flag");
return Err(1); return Err(1);
} }
profile = candidate.to_string(); profile = candidate.to_string();
continue; continue;
} }
warn("Unknown build flag"); warn!("Unknown build flag");
return Err(1); return Err(1);
} }

View File

@@ -15,8 +15,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::utils::log::error; use crate::prelude::*;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
use crate::utils::cli_styles::{
BOLD_GREEN, Colorize, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST, SUCCESS_ST,
};
use crate::utils::log::sprintln;
use glob::glob; use glob::glob;
use std::process::Command; use std::process::Command;
@@ -28,10 +32,10 @@ use std::process::Command;
/// If the first argument is `--help`, prints usage information and returns 0. /// If the first argument is `--help`, prints usage information and returns 0.
pub fn fmt(args: &[String]) -> i32 { pub fn fmt(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr fmt", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr fmt");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Formats all C/C++ source files using clang-format."); println!(" Formats all C/C++ source files using clang-format.");
println!(" Scans src/ and tests/ directories recursively."); println!(" Scans src/ and tests/ directories recursively.");
return 0; return 0;
@@ -69,14 +73,14 @@ pub fn fmt(args: &[String]) -> i32 {
} }
if files.is_empty() { if files.is_empty() {
println!(" {} No files to format", colored("Format", BOLD_GREEN)); println!(" {} No files to format", "Format".style(BOLD_GREEN));
return 0; return 0;
} }
// Format the collected files // Format the collected files
println!( println!(
" {} {} files", " {} {} files",
colored("Formatting", BOLD_GREEN), "Formatting".style(BOLD_GREEN),
files.len() files.len()
); );
@@ -84,15 +88,15 @@ pub fn fmt(args: &[String]) -> i32 {
match status { match status {
Ok(s) if s.success() => { Ok(s) if s.success() => {
println!(" {} successful", colored("Format", BOLD_GREEN)); println!(" {} successful", "Format".style(SUCCESS_ST));
0 0
} }
Ok(s) => { Ok(s) => {
error(&format!("clang-format failed with status: {}", s)); error!("clang-format failed with status: {s}");
1 1
} }
Err(e) => { Err(e) => {
error(&format!("Failed to execute clang-format: {}", e)); error!("Failed to execute clang-format: {e}");
1 1
} }
} }

View File

@@ -15,6 +15,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::core::build::builder::BuildContext; use crate::core::build::builder::BuildContext;
use crate::core::build::builder::artifact::{absolute_artifact_path, resolve_artifact_path}; use crate::core::build::builder::artifact::{absolute_artifact_path, resolve_artifact_path};
use crate::core::build::builder::collect_sources; use crate::core::build::builder::collect_sources;
@@ -26,11 +28,11 @@ use crate::utils::build::{
get_list_with_profile, get_string_with_profile, normalize_kind, normalize_platform, get_list_with_profile, get_string_with_profile, normalize_kind, normalize_platform,
resolve_artifact_target_dir, resolve_compiler, resolve_pkg_config_flags_lossy, resolve_artifact_target_dir, resolve_compiler, resolve_pkg_config_flags_lossy,
}; };
use crate::utils::cli_styles::{HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST};
use crate::utils::fs::{ use crate::utils::fs::{
absolute_join, atomic_write, canonicalize_path, ensure_dcr_dir, find_project_root, absolute_join, atomic_write, canonicalize_path, ensure_dcr_dir, find_project_root,
}; };
use crate::utils::log::error; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, printc};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@@ -251,29 +253,29 @@ pub struct ProjectInfo {
pub fn r#gen(args: &[String]) -> i32 { pub fn r#gen(args: &[String]) -> i32 {
let subcommand = match args.first() { let subcommand = match args.first() {
Some(s) if s == "--help" => { Some(s) if s == "--help" => {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr gen <subcommand>", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr gen <subcommand>");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Generates IDE and tooling integration files."); println!(" Generates IDE and tooling integration files.");
println!(); println!();
printc("SUBCOMMANDS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "SUBCOMMANDS:");
println!(" project-info Print project metadata as JSON"); println!(" project-info Print project metadata as JSON");
println!(" compile-commands Generate .dcr/compile_commands.json"); println!(" compile-commands Generate .dcr/compile_commands.json");
println!(" vscode Generate .vscode/ integration files"); println!(" vscode Generate .vscode/ integration files");
println!(" clion Generate .idea/ integration files"); println!(" clion Generate .idea/ integration files");
println!(); println!();
printc("OPTIONS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "OPTIONS:");
println!(" --debug | --release Profile (default: debug)"); println!(" --debug | --release Profile (default: debug)");
println!(" --quiet | -q Suppress success messages on stdout"); println!(" --quiet | -q Suppress success messages on stdout");
return 0; return 0;
} }
Some(s) => s.as_str(), Some(s) => s.as_str(),
None => { None => {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr gen <subcommand>", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr gen <subcommand>");
println!(); println!();
printc("SUBCOMMANDS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "SUBCOMMANDS:");
println!(" project-info Print project metadata as JSON"); println!(" project-info Print project metadata as JSON");
println!(" compile-commands Generate .dcr/compile_commands.json"); println!(" compile-commands Generate .dcr/compile_commands.json");
println!(" vscode Generate .vscode/ integration files"); println!(" vscode Generate .vscode/ integration files");
@@ -290,7 +292,7 @@ pub fn r#gen(args: &[String]) -> i32 {
"vscode" => gen_vscode(rest), "vscode" => gen_vscode(rest),
"clion" => gen_clion(rest), "clion" => gen_clion(rest),
_ => { _ => {
error(&format!("Unknown gen subcommand: {subcommand}")); error!("Unknown gen subcommand: {subcommand}");
1 1
} }
} }
@@ -853,7 +855,7 @@ fn gen_project_info(args: &[String]) -> i32 {
let all = match collect_all(&root, &profile) { let all = match collect_all(&root, &profile) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
error(&e); error!("{e}");
return 1; return 1;
} }
}; };
@@ -975,7 +977,7 @@ fn gen_compile_commands(args: &[String]) -> i32 {
let all = match collect_all(&root, &profile) { let all = match collect_all(&root, &profile) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
error(&e); error!("{e}");
return 1; return 1;
} }
}; };
@@ -988,7 +990,7 @@ fn gen_compile_commands(args: &[String]) -> i32 {
/// Used by `compile-commands`, `vscode`, and `clion`. /// Used by `compile-commands`, `vscode`, and `clion`.
fn gen_compile_commands_inner(root: &Path, profile: &str, all: &[ProjectInfo], quiet: bool) -> i32 { fn gen_compile_commands_inner(root: &Path, profile: &str, all: &[ProjectInfo], quiet: bool) -> i32 {
if let Err(e) = ensure_dcr_dir(root) { if let Err(e) = ensure_dcr_dir(root) {
error(&format!("Failed to create .dcr/: {e}")); error!("Failed to create .dcr/: {e}");
return 1; return 1;
} }
@@ -1009,7 +1011,7 @@ fn gen_compile_commands_inner(root: &Path, profile: &str, all: &[ProjectInfo], q
0 0
} }
Err(e) => { Err(e) => {
error(&format!("Failed to write compile_commands.json: {e}")); error!("Failed to write compile_commands.json: {e}");
1 1
} }
} }
@@ -1163,7 +1165,7 @@ fn gen_vscode(args: &[String]) -> i32 {
let all = match collect_all(&root, &profile) { let all = match collect_all(&root, &profile) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
error(&e); error!("{e}");
return 1; return 1;
} }
}; };
@@ -1176,13 +1178,13 @@ fn gen_vscode(args: &[String]) -> i32 {
let vscode_dir = root.join(".vscode"); let vscode_dir = root.join(".vscode");
if let Err(e) = std::fs::create_dir_all(&vscode_dir) { if let Err(e) = std::fs::create_dir_all(&vscode_dir) {
error(&format!("Failed to create .vscode/: {e}")); error!("Failed to create .vscode/: {e}");
return 1; return 1;
} }
// tasks.json // tasks.json
if let Err(e) = std::fs::write(vscode_dir.join("tasks.json"), gen_tasks_json()) { if let Err(e) = std::fs::write(vscode_dir.join("tasks.json"), gen_tasks_json()) {
error(&format!("Failed to write tasks.json: {e}")); error!("Failed to write tasks.json: {e}");
return 1; return 1;
} }
println!("Generated {}", vscode_dir.join("tasks.json").display()); println!("Generated {}", vscode_dir.join("tasks.json").display());
@@ -1190,7 +1192,7 @@ fn gen_vscode(args: &[String]) -> i32 {
// launch.json — one entry per binary target // launch.json — one entry per binary target
let launch = gen_launch_json(&all, &root); let launch = gen_launch_json(&all, &root);
if let Err(e) = std::fs::write(vscode_dir.join("launch.json"), launch) { if let Err(e) = std::fs::write(vscode_dir.join("launch.json"), launch) {
error(&format!("Failed to write launch.json: {e}")); error!("Failed to write launch.json: {e}");
return 1; return 1;
} }
println!("Generated {}", vscode_dir.join("launch.json").display()); println!("Generated {}", vscode_dir.join("launch.json").display());
@@ -1198,14 +1200,14 @@ fn gen_vscode(args: &[String]) -> i32 {
// settings.json (clangd compile-commands-dir) // settings.json (clangd compile-commands-dir)
let settings = gen_settings_json(&root); let settings = gen_settings_json(&root);
if let Err(e) = std::fs::write(vscode_dir.join("settings.json"), settings) { if let Err(e) = std::fs::write(vscode_dir.join("settings.json"), settings) {
error(&format!("Failed to write settings.json: {e}")); error!("Failed to write settings.json: {e}");
return 1; return 1;
} }
println!("Generated {}", vscode_dir.join("settings.json").display()); println!("Generated {}", vscode_dir.join("settings.json").display());
// extensions.json — disable cpptools, recommend clangd // extensions.json — disable cpptools, recommend clangd
if let Err(e) = std::fs::write(vscode_dir.join("extensions.json"), gen_extensions_json()) { if let Err(e) = std::fs::write(vscode_dir.join("extensions.json"), gen_extensions_json()) {
error(&format!("Failed to write extensions.json: {e}")); error!("Failed to write extensions.json: {e}");
return 1; return 1;
} }
println!("Generated {}", vscode_dir.join("extensions.json").display()); println!("Generated {}", vscode_dir.join("extensions.json").display());
@@ -1453,7 +1455,7 @@ fn gen_clion(args: &[String]) -> i32 {
let all = match collect_all(&root, &profile) { let all = match collect_all(&root, &profile) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
error(&e); error!("{e}");
return 1; return 1;
} }
}; };
@@ -1466,19 +1468,19 @@ fn gen_clion(args: &[String]) -> i32 {
let idea_dir = root.join(".idea"); let idea_dir = root.join(".idea");
if let Err(e) = std::fs::create_dir_all(&idea_dir) { if let Err(e) = std::fs::create_dir_all(&idea_dir) {
error(&format!("Failed to create .idea/: {e}")); error!("Failed to create .idea/: {e}");
return 1; return 1;
} }
let run_configs_dir = idea_dir.join("runConfigurations"); let run_configs_dir = idea_dir.join("runConfigurations");
if let Err(e) = std::fs::create_dir_all(&run_configs_dir) { if let Err(e) = std::fs::create_dir_all(&run_configs_dir) {
error(&format!("Failed to create .idea/runConfigurations/: {e}")); error!("Failed to create .idea/runConfigurations/: {e}");
return 1; return 1;
} }
// externalTools.xml // externalTools.xml
let ext_tools = gen_clion_external_tools(); let ext_tools = gen_clion_external_tools();
if let Err(e) = std::fs::write(idea_dir.join("externalTools.xml"), ext_tools) { if let Err(e) = std::fs::write(idea_dir.join("externalTools.xml"), ext_tools) {
error(&format!("Failed to write externalTools.xml: {e}")); error!("Failed to write externalTools.xml: {e}");
return 1; return 1;
} }
println!("Generated {}", idea_dir.join("externalTools.xml").display()); println!("Generated {}", idea_dir.join("externalTools.xml").display());
@@ -1486,7 +1488,7 @@ fn gen_clion(args: &[String]) -> i32 {
// customTargets.xml // customTargets.xml
let targets = gen_clion_custom_targets(); let targets = gen_clion_custom_targets();
if let Err(e) = std::fs::write(idea_dir.join("customTargets.xml"), targets) { if let Err(e) = std::fs::write(idea_dir.join("customTargets.xml"), targets) {
error(&format!("Failed to write customTargets.xml: {e}")); error!("Failed to write customTargets.xml: {e}");
return 1; return 1;
} }
println!("Generated {}", idea_dir.join("customTargets.xml").display()); println!("Generated {}", idea_dir.join("customTargets.xml").display());
@@ -1494,7 +1496,7 @@ fn gen_clion(args: &[String]) -> i32 {
// misc.xml — point CLion at compile_commands.json // misc.xml — point CLion at compile_commands.json
let misc = gen_clion_misc_xml(&root); let misc = gen_clion_misc_xml(&root);
if let Err(e) = std::fs::write(idea_dir.join("misc.xml"), misc) { if let Err(e) = std::fs::write(idea_dir.join("misc.xml"), misc) {
error(&format!("Failed to write misc.xml: {e}")); error!("Failed to write misc.xml: {e}");
return 1; return 1;
} }
println!("Generated {}", idea_dir.join("misc.xml").display()); println!("Generated {}", idea_dir.join("misc.xml").display());
@@ -1502,7 +1504,7 @@ fn gen_clion(args: &[String]) -> i32 {
// .idea/.gitignore // .idea/.gitignore
let gitignore = "# CLion generated files\nworkspace.xml\n*.iml\n"; let gitignore = "# CLion generated files\nworkspace.xml\n*.iml\n";
if let Err(e) = std::fs::write(idea_dir.join(".gitignore"), gitignore) { if let Err(e) = std::fs::write(idea_dir.join(".gitignore"), gitignore) {
error(&format!("Failed to write .idea/.gitignore: {e}")); error!("Failed to write .idea/.gitignore: {e}");
return 1; return 1;
} }
println!("Generated {}", idea_dir.join(".gitignore").display()); println!("Generated {}", idea_dir.join(".gitignore").display());
@@ -1516,7 +1518,7 @@ fn gen_clion(args: &[String]) -> i32 {
let fname = format!("{}.xml", sanitize_filename(&info.name)); let fname = format!("{}.xml", sanitize_filename(&info.name));
let path = run_configs_dir.join(&fname); let path = run_configs_dir.join(&fname);
if let Err(e) = std::fs::write(&path, xml) { if let Err(e) = std::fs::write(&path, xml) {
error(&format!("Failed to write runConfigurations/{fname}: {e}")); error!("Failed to write runConfigurations/{fname}: {e}");
return 1; return 1;
} }
println!("Generated {}", path.display()); println!("Generated {}", path.display());
@@ -1720,18 +1722,18 @@ fn parse_gen_args(args: &[String]) -> Result<(PathBuf, String, bool), i32> {
} }
let start = std::env::current_dir().map_err(|_| { let start = std::env::current_dir().map_err(|_| {
error("Failed to determine current directory"); error!("Failed to determine current directory");
1i32 1i32
})?; })?;
let root = match find_project_root(&start) { let root = match find_project_root(&start) {
Ok(Some(r)) => canonicalize_path(&r), Ok(Some(r)) => canonicalize_path(&r),
Ok(None) => { Ok(None) => {
error("dcr.toml not found"); error!("dcr.toml not found");
return Err(1); return Err(1);
} }
Err(_) => { Err(_) => {
error("Failed to find project root"); error!("Failed to find project root");
return Err(1); return Err(1);
} }
}; };

View File

@@ -15,7 +15,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, printc}; use crate::utils::cli_styles::{HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST};
use crate::utils::log::sprintln;
/// Displays the help information for the DCR CLI tool. /// Displays the help information for the DCR CLI tool.
/// ///
@@ -27,10 +28,10 @@ pub fn help() -> i32 {
println!("DCR (Dexoron Cargo Realization)"); println!("DCR (Dexoron Cargo Realization)");
println!("C project manager inspired by Cargo."); println!("C project manager inspired by Cargo.");
println!(); println!();
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr <command> [options]", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr <command> [options]");
println!(); println!();
printc("COMMANDS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "COMMANDS:");
println!(" new <name> Create a new project"); println!(" new <name> Create a new project");
println!(" init Initialize the current directory as a project"); println!(" init Initialize the current directory as a project");
println!(" build [--profile] Build the project (default: --debug)"); println!(" build [--profile] Build the project (default: --debug)");
@@ -43,24 +44,24 @@ pub fn help() -> i32 {
println!(" setup Show configured package registries"); println!(" setup Show configured package registries");
println!(" clean Remove the target directory"); println!(" clean Remove the target directory");
println!(" gen <subcommand> Generate IDE integration files"); println!(" gen <subcommand> Generate IDE integration files");
printc("FLAGS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "FLAGS:");
println!(" --help Show command help"); println!(" --help Show command help");
println!(" --update Update dcr to the latest version"); println!(" --update Update dcr to the latest version");
println!(" --version Show dcr version"); println!(" --version Show dcr version");
println!(); println!();
printc("OPTIONS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "OPTIONS:");
println!(" --debug Use debug profile"); println!(" --debug Use debug profile");
println!(" --release Use release profile"); println!(" --release Use release profile");
println!(" --force Force rebuild (build/run)"); println!(" --force Force rebuild (build/run)");
println!(" --clean Clean before build (build/run)"); println!(" --clean Clean before build (build/run)");
println!(" --all Clean all workspace members (clean)"); println!(" --all Clean all workspace members (clean)");
println!(); println!();
printc("EXAMPLES:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "EXAMPLES:");
printc(" dcr new hello", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr new hello");
printc(" dcr build --release", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr build --release");
printc(" dcr run --debug", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr run --debug");
println!(); println!();
printc("TIP:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "TIP:");
println!(" Run 'dcr <command> --help' for command-specific help."); println!(" Run 'dcr <command> --help' for command-specific help.");
0 0
} }

View File

@@ -15,14 +15,17 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::config::FILE_MAIN_C; use crate::config::FILE_MAIN_C;
use crate::core::build_config::{Config, validate_package_name}; use crate::core::build_config::{Config, validate_package_name};
use crate::core::vcs::VcsKind; use crate::core::vcs::VcsKind;
use crate::utils::cli_styles::{
BOLD_CYAN, BOLD_GREEN, Colorize, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST, SUCCESS_ST,
};
use crate::utils::fs::check_dir; use crate::utils::fs::check_dir;
use crate::utils::log::{error, warn}; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc}; use std::{fs, io::Write};
use std::fs;
use std::io::Write;
/// Initializes the current working directory as a new DCR project. /// Initializes the current working directory as a new DCR project.
/// ///
@@ -30,10 +33,10 @@ use std::io::Write;
/// `dcr.toml` and `src/main.c`, and configures version control (Git) integration if applicable. /// `dcr.toml` and `src/main.c`, and configures version control (Git) integration if applicable.
pub fn init(args: &[String]) -> i32 { pub fn init(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr init [--vcs <git|none>]", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr init [--vcs <git|none>]");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Initializes the current directory as a DCR project."); println!(" Initializes the current directory as a DCR project.");
println!(" The directory must be empty."); println!(" The directory must be empty.");
return 0; return 0;
@@ -48,7 +51,7 @@ pub fn init(args: &[String]) -> i32 {
if let Some(val) = iter.next() { if let Some(val) = iter.next() {
vcs_str = Some(val.clone()); vcs_str = Some(val.clone());
} else { } else {
error("--vcs requires a value"); error!("--vcs requires a value");
return 1; return 1;
} }
} else if let Some(stripped) = arg.strip_prefix("--vcs=") { } else if let Some(stripped) = arg.strip_prefix("--vcs=") {
@@ -59,7 +62,7 @@ pub fn init(args: &[String]) -> i32 {
} }
if !clean_args.is_empty() { if !clean_args.is_empty() {
warn("Command does not support additional arguments"); warn!("Command does not support additional arguments");
return 1; return 1;
} }
@@ -71,16 +74,15 @@ pub fn init(args: &[String]) -> i32 {
.unwrap_or_else(|| "project".to_string()); .unwrap_or_else(|| "project".to_string());
if !items.is_empty() { if !items.is_empty() {
error("Directory not empty"); error!("Directory not empty");
return 1; return 1;
} }
if let Err(e) = validate_package_name(&project_name) { if let Err(e) = validate_package_name(&project_name) {
error(&format!( error!(
"Invalid project name `{}`: {}", "Invalid project name `{}`: {e}",
colored(&project_name, BOLD_CYAN), project_name.style(BOLD_CYAN),
e );
));
return 1; return 1;
} }
@@ -91,35 +93,35 @@ pub fn init(args: &[String]) -> i32 {
// Create and initialize default manifest (dcr.toml). // Create and initialize default manifest (dcr.toml).
if Config::create("./dcr.toml", Some(&project_name)).is_err() { if Config::create("./dcr.toml", Some(&project_name)).is_err() {
error("Failed to create dcr.toml"); error!("Failed to create dcr.toml");
return 1; return 1;
} }
println!( println!(
" {} Created file {}", " {} Created file {}",
colored("✔", BOLD_GREEN), "✔".style(SUCCESS_ST),
colored("dcr.toml", BOLD_CYAN) "dcr.toml".style(BOLD_CYAN)
); );
// Create src directory and write template main.c file. // Create src directory and write template main.c file.
if fs::create_dir("src").is_err() { if fs::create_dir("src").is_err() {
error("Failed to create src/"); error!("Failed to create src/");
return 1; return 1;
} }
let mut main_c = match fs::File::create("./src/main.c") { let mut main_c = match fs::File::create("./src/main.c") {
Ok(file) => file, Ok(file) => file,
Err(_) => { Err(_) => {
error("Failed to create src/main.c"); error!("Failed to create src/main.c");
return 1; return 1;
} }
}; };
if main_c.write_all(FILE_MAIN_C.as_bytes()).is_err() { if main_c.write_all(FILE_MAIN_C.as_bytes()).is_err() {
error("Failed to write src/main.c"); error!("Failed to write src/main.c");
return 1; return 1;
} }
println!( println!(
" {} Created file {}", " {} Created file {}",
colored("✔", BOLD_GREEN), "✔".style(SUCCESS_ST),
colored("src/main.c", BOLD_CYAN) "src/main.c".style(BOLD_CYAN)
); );
// Resolve target VCS provider or detect existing repositories. // Resolve target VCS provider or detect existing repositories.
@@ -128,7 +130,7 @@ pub fn init(args: &[String]) -> i32 {
match VcsKind::parse(vcs_val) { match VcsKind::parse(vcs_val) {
Ok(kind) => vcs_kind = kind, Ok(kind) => vcs_kind = kind,
Err(e) => { Err(e) => {
error(&e); error!("{e}");
return 1; return 1;
} }
} }
@@ -142,29 +144,26 @@ pub fn init(args: &[String]) -> i32 {
if crate::utils::git::is_git_available() { if crate::utils::git::is_git_available() {
let project_path = std::path::Path::new("."); let project_path = std::path::Path::new(".");
if let Err(e) = crate::core::vcs::init_vcs(vcs_kind, project_path) { if let Err(e) = crate::core::vcs::init_vcs(vcs_kind, project_path) {
warn(&format!("Failed to initialize git repository: {}", e)); warn!("Failed to initialize git repository: {e}");
} else { } else {
println!( println!(" {} Initialized git repository", "✔".style(SUCCESS_ST));
" {} Initialized git repository",
colored("✔", BOLD_GREEN)
);
} }
} else { } else {
warn( warn!(
"Git is not installed or not found in PATH. Skipping Git repository initialization.", "Git is not installed or not found in PATH. Skipping Git repository initialization.",
); );
} }
} }
if let Err(e) = crate::utils::fs::ensure_gitignore_has_dcr(std::path::Path::new(".")) { if let Err(e) = crate::utils::fs::ensure_gitignore_has_dcr(std::path::Path::new(".")) {
warn(&format!("Failed to update .gitignore: {e}")); warn!("Failed to update .gitignore: {e}");
} }
println!( println!(
"Project `{}` successfully created\n", "Project `{}` successfully created\n",
colored(project_name.as_str(), BOLD_GREEN) project_name.style(SUCCESS_ST)
); );
printc("Next step:", BOLD_GREEN); sprintln!(BOLD_GREEN, "Next step:");
printc(" dcr run", BOLD_CYAN); sprintln!(BOLD_CYAN, " dcr run");
0 0
} }

View File

@@ -15,12 +15,16 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::core::build::common; use crate::core::build::common;
use crate::core::build_config::Config; use crate::core::build_config::Config;
use crate::utils::build::{get_bool_with_profile, get_list_with_profile}; use crate::utils::build::{get_bool_with_profile, get_list_with_profile};
use crate::utils::cli_styles::{
ALERT_ST, BOLD_GREEN, Colorize, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST, SUCCESS_ST,
};
use crate::utils::fs::find_project_root; use crate::utils::fs::find_project_root;
use crate::utils::log::error; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, BOLD_YELLOW, colored, printc};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
@@ -29,14 +33,14 @@ use std::process::Command;
/// Scans source roots (configurable via build.roots) and tests/ directory. /// Scans source roots (configurable via build.roots) and tests/ directory.
pub fn lint(args: &[String]) -> i32 { pub fn lint(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr lint [--fix]", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr lint [--fix]");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Runs clang-tidy on all C/C++ source files."); println!(" Runs clang-tidy on all C/C++ source files.");
println!(" Scans source roots (configurable via build.roots) and tests/ directory."); println!(" Scans source roots (configurable via build.roots) and tests/ directory.");
println!(); println!();
printc("OPTIONS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "OPTIONS:");
println!(" --fix Apply clang-tidy suggestions automatically"); println!(" --fix Apply clang-tidy suggestions automatically");
return 0; return 0;
} }
@@ -46,7 +50,7 @@ pub fn lint(args: &[String]) -> i32 {
let start_dir = match std::env::current_dir() { let start_dir = match std::env::current_dir() {
Ok(dir) => dir, Ok(dir) => dir,
Err(_) => { Err(_) => {
error("Failed to determine current directory"); error!("Failed to determine current directory");
return 1; return 1;
} }
}; };
@@ -54,11 +58,11 @@ pub fn lint(args: &[String]) -> i32 {
let root = match find_project_root(&start_dir) { let root = match find_project_root(&start_dir) {
Ok(Some(r)) => r, Ok(Some(r)) => r,
Ok(None) => { Ok(None) => {
error("dcr.toml not found"); error!("dcr.toml not found");
return 1; return 1;
} }
Err(_) => { Err(_) => {
error("Failed to find project root"); error!("Failed to find project root");
return 1; return 1;
} }
}; };
@@ -66,7 +70,7 @@ pub fn lint(args: &[String]) -> i32 {
let config = match Config::open(&root.join("dcr.toml").to_string_lossy()) { let config = match Config::open(&root.join("dcr.toml").to_string_lossy()) {
Ok(c) => c, Ok(c) => c,
Err(e) => { Err(e) => {
error(&format!("Failed to load dcr.toml: {e}")); error!("Failed to load dcr.toml: {e}");
return 1; return 1;
} }
}; };
@@ -98,7 +102,7 @@ pub fn lint(args: &[String]) -> i32 {
match common::collect_sources(&roots, &cxx_extensions, &exclude_dirs, &include_paths) { match common::collect_sources(&roots, &cxx_extensions, &exclude_dirs, &include_paths) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
error(&format!("Failed to collect source files: {e}")); error!("Failed to collect source files: {e}");
return 1; return 1;
} }
}; };
@@ -116,13 +120,13 @@ pub fn lint(args: &[String]) -> i32 {
files.sort(); files.sort();
if files.is_empty() { if files.is_empty() {
println!(" {} No files to lint", colored("Lint", BOLD_GREEN)); println!(" {} No files to lint", "Lint".style(BOLD_GREEN));
return 0; return 0;
} }
println!( println!(
" {} {} files {}", " {} {} files {}",
colored("Linting", BOLD_GREEN), "Linting".style(BOLD_GREEN),
files.len(), files.len(),
if do_fix { "(with --fix)" } else { "" } if do_fix { "(with --fix)" } else { "" }
); );
@@ -142,7 +146,7 @@ pub fn lint(args: &[String]) -> i32 {
let output = match cmd.output() { let output = match cmd.output() {
Ok(o) => o, Ok(o) => o,
Err(e) => { Err(e) => {
error(&format!("Failed to execute clang-tidy: {}", e)); error!("Failed to execute clang-tidy: {e}");
return 1; return 1;
} }
}; };
@@ -160,13 +164,13 @@ pub fn lint(args: &[String]) -> i32 {
if issue_count > 0 { if issue_count > 0 {
total_issues += issue_count as u32; total_issues += issue_count as u32;
failed += 1; failed += 1;
print!(" {} ", colored("×", BOLD_YELLOW)); print!(" {} ", "×".style(ALERT_ST));
println!("{} ({} issues)", file, issue_count); println!("{} ({} issues)", file, issue_count);
for issue in &issues { for issue in &issues {
println!(" {}", issue); println!(" {}", issue);
} }
} else { } else {
print!(" {} ", colored("✓", BOLD_GREEN)); print!(" {} ", "✓".style(SUCCESS_ST));
println!("{}", file); println!("{}", file);
} }
} }
@@ -177,13 +181,13 @@ pub fn lint(args: &[String]) -> i32 {
if failed == 0 { if failed == 0 {
println!( println!(
" {} {} files, no issues", " {} {} files, no issues",
colored("Lint", BOLD_GREEN), "Lint".style(SUCCESS_ST),
total total
); );
} else { } else {
println!( println!(
" {} {}/{} files passed, {} issues found", " {} {}/{} files passed, {} issues found",
colored("Lint", BOLD_YELLOW), "Lint".style(ALERT_ST),
passed, passed,
total, total,
total_issues total_issues

View File

@@ -15,14 +15,17 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::config::FILE_MAIN_C; use crate::config::FILE_MAIN_C;
use crate::core::build_config::{Config, validate_package_name}; use crate::core::build_config::{Config, validate_package_name};
use crate::core::vcs::VcsKind; use crate::core::vcs::VcsKind;
use crate::utils::cli_styles::{
BOLD_CYAN, BOLD_GREEN, Colorize, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST, SUCCESS_ST,
};
use crate::utils::fs::check_dir; use crate::utils::fs::check_dir;
use crate::utils::log::{error, warn}; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc}; use std::{fs, io::Write};
use std::fs;
use std::io::Write;
/// Executes the `dcr new` command to scaffold a new C/C++ project directory structure. /// Executes the `dcr new` command to scaffold a new C/C++ project directory structure.
/// ///
@@ -30,10 +33,10 @@ use std::io::Write;
/// template source file generation (`src/main.c`), and initial VCS setup. /// template source file generation (`src/main.c`), and initial VCS setup.
pub fn new(args: &[String]) -> i32 { pub fn new(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr new <name> [--vcs <git|none>]", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr new <name> [--vcs <git|none>]");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Creates a new C/C++ project with the given name."); println!(" Creates a new C/C++ project with the given name.");
println!(" The name may only contain ASCII letters, digits, '_' and '-'."); println!(" The name may only contain ASCII letters, digits, '_' and '-'.");
return 0; return 0;
@@ -50,7 +53,7 @@ pub fn new(args: &[String]) -> i32 {
if let Some(val) = iter.next() { if let Some(val) = iter.next() {
vcs_str = Some(val.clone()); vcs_str = Some(val.clone());
} else { } else {
error("--vcs requires a value"); error!("--vcs requires a value");
return 1; return 1;
} }
} else if let Some(stripped) = arg.strip_prefix("--vcs=") { } else if let Some(stripped) = arg.strip_prefix("--vcs=") {
@@ -61,85 +64,81 @@ pub fn new(args: &[String]) -> i32 {
} }
if clean_args.is_empty() { if clean_args.is_empty() {
error("Project name not specified"); error!("Project name not specified");
return 1; return 1;
} }
if clean_args.len() > 1 { if clean_args.len() > 1 {
warn("Command does not support additional arguments"); warn!("Command does not support additional arguments");
return 1; return 1;
} }
let project_name = &clean_args[0]; let project_name = &clean_args[0];
println!( println!("Creating a Project `{}`...", project_name.style(BOLD_CYAN));
"Creating a Project `{}`...",
colored(project_name, BOLD_CYAN)
);
if let Err(e) = validate_package_name(project_name) { if let Err(e) = validate_package_name(project_name) {
error(&format!( error!(
"Invalid project name `{}`: {}", "Invalid project name `{}`: {e}",
colored(project_name, BOLD_CYAN), project_name.style(BOLD_CYAN),
e );
));
return 1; return 1;
} }
if items.contains(project_name) { if items.contains(project_name) {
error(&format!( error!(
"Directory `{}` already exists\n", "Directory `{}` already exists\n",
colored(project_name, BOLD_CYAN) project_name.style(BOLD_CYAN)
)); );
printc("Hint:", BOLD_CYAN); sprintln!(BOLD_CYAN, "Hint:");
println!( println!(
" Use `{}` to initialize an existing project\n or specify a different project name", " Use `{}` to initialize an existing project\n or specify a different project name",
colored("dcr init", BOLD_CYAN) "dcr init".style(BOLD_CYAN)
); );
return 1; return 1;
} }
if fs::create_dir(project_name).is_err() { if fs::create_dir(project_name).is_err() {
error("Failed to create directory"); error!("Failed to create directory");
return 1; return 1;
} }
println!( println!(
" {} Directory created {}", " {} Directory created {}",
colored("✔", BOLD_GREEN), "✔".style(SUCCESS_ST),
project_name project_name
); );
// Initialize and write project configuration manifest (dcr.toml). // Initialize and write project configuration manifest (dcr.toml).
let toml_path = format!("./{project_name}/dcr.toml"); let toml_path = format!("./{project_name}/dcr.toml");
if Config::create(&toml_path, Some(project_name)).is_err() { if Config::create(&toml_path, Some(project_name)).is_err() {
error("Failed to create dcr.toml"); error!("Failed to create dcr.toml");
return 1; return 1;
} }
println!( println!(
" {} Created file {}", " {} Created file {}",
colored("✔", BOLD_GREEN), "✔".style(SUCCESS_ST),
colored("dcr.toml", BOLD_CYAN) "dcr.toml".style(BOLD_CYAN)
); );
// Generate src directory and populate initial main.c file. // Generate src directory and populate initial main.c file.
if fs::create_dir_all(format!("./{project_name}/src")).is_err() { if fs::create_dir_all(format!("./{project_name}/src")).is_err() {
error("Failed to create src/"); error!("Failed to create src/");
return 1; return 1;
} }
let main_c_path = format!("./{project_name}/src/main.c"); let main_c_path = format!("./{project_name}/src/main.c");
let mut main_c = match fs::File::create(&main_c_path) { let mut main_c = match fs::File::create(&main_c_path) {
Ok(file) => file, Ok(file) => file,
Err(_) => { Err(_) => {
error("Failed to create src/main.c"); error!("Failed to create src/main.c");
return 1; return 1;
} }
}; };
if main_c.write_all(FILE_MAIN_C.as_bytes()).is_err() { if main_c.write_all(FILE_MAIN_C.as_bytes()).is_err() {
error("Failed to write src/main.c"); error!("Failed to write src/main.c");
return 1; return 1;
} }
println!( println!(
" {} Created file {}", " {} Created file {}",
colored("✔", BOLD_GREEN), "✔".style(SUCCESS_ST),
colored("src/main.c", BOLD_CYAN) "src/main.c".style(BOLD_CYAN)
); );
// Resolve target VCS mode based on CLI flags or parent directory state. // Resolve target VCS mode based on CLI flags or parent directory state.
@@ -148,7 +147,8 @@ pub fn new(args: &[String]) -> i32 {
match VcsKind::parse(vcs_val) { match VcsKind::parse(vcs_val) {
Ok(kind) => vcs_kind = kind, Ok(kind) => vcs_kind = kind,
Err(e) => { Err(e) => {
error(&e); error!("{e}");
return 1; return 1;
} }
} }
@@ -162,15 +162,12 @@ pub fn new(args: &[String]) -> i32 {
if crate::utils::git::is_git_available() { if crate::utils::git::is_git_available() {
let project_path = std::path::Path::new(project_name); let project_path = std::path::Path::new(project_name);
if let Err(e) = crate::core::vcs::init_vcs(vcs_kind, project_path) { if let Err(e) = crate::core::vcs::init_vcs(vcs_kind, project_path) {
warn(&format!("Failed to initialize git repository: {}", e)); warn!("Failed to initialize git repository: {e}");
} else { } else {
println!( println!(" {} Initialized git repository", "✔".style(SUCCESS_ST));
" {} Initialized git repository",
colored("✔", BOLD_GREEN)
);
} }
} else { } else {
warn( warn!(
"Git is not installed or not found in PATH. Skipping Git repository initialization.", "Git is not installed or not found in PATH. Skipping Git repository initialization.",
); );
} }
@@ -178,14 +175,14 @@ pub fn new(args: &[String]) -> i32 {
let project_path = std::path::Path::new(project_name); let project_path = std::path::Path::new(project_name);
if let Err(e) = crate::utils::fs::ensure_gitignore_has_dcr(project_path) { if let Err(e) = crate::utils::fs::ensure_gitignore_has_dcr(project_path) {
warn(&format!("Failed to update .gitignore: {e}")); warn!("Failed to update .gitignore: {e}");
} }
println!( println!(
"Project `{}` successfully created\n", "Project `{}` successfully created\n",
colored(project_name, BOLD_GREEN) project_name.style(SUCCESS_ST)
); );
printc("Next step:", BOLD_GREEN); sprintln!(BOLD_GREEN, "Next step:");
printc(&format!(" cd {}\n dcr run", project_name), BOLD_CYAN); sprintln!(BOLD_CYAN, " cd {}\n dcr run", project_name);
0 0
} }

View File

@@ -15,17 +15,17 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::cli::build::build; use crate::cli::build::build;
use crate::cli::flags::parse_build_run_flags; use crate::cli::flags::parse_build_run_flags;
use crate::core::build_config::Config; use crate::core::build_config::Config;
use crate::core::runner::run_binary; use crate::core::runner::run_binary;
use crate::utils::build::{normalize_target_os, parse_version_info, substitute_vars}; use crate::utils::build::{normalize_target_os, parse_version_info, substitute_vars};
use crate::utils::fs::find_project_root; use crate::utils::cli_styles::{BOLD_GREEN, Colorize, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST};
use crate::utils::fs::with_dir; use crate::utils::fs::{find_project_root, with_dir};
use crate::utils::log::error; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc}; use std::{path::Path, process::Command};
use std::path::Path;
use std::process::Command;
/// Retrieves the run command from the config, preferring target-specific, /// Retrieves the run command from the config, preferring target-specific,
/// then profile-specific, then the base `run.cmd`. /// then profile-specific, then the base `run.cmd`.
@@ -67,17 +67,17 @@ fn get_run_cmd(
/// Process exit code from the built binary / `run.cmd`, or non-zero on build/setup failure. /// Process exit code from the built binary / `run.cmd`, or non-zero on build/setup failure.
pub fn run(args: &[String]) -> i32 { pub fn run(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc( sprintln!(
" dcr run [--debug | --release] [--target <triple>] [--force] [--clean] [--verbose] [-- <args>...]", HELP_EXAMPLES_ST,
BOLD_CYAN, " dcr run [--debug | --release] [--target <triple>] [--force] [--clean] [--verbose] [-- <args>...]"
); );
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Builds and runs the project. Only available for kind = \"bin\"."); println!(" Builds and runs the project. Only available for kind = \"bin\".");
println!(" Arguments after `--` are passed to the built binary (cargo-style)."); println!(" Arguments after `--` are passed to the built binary (cargo-style).");
println!(); println!();
printc("OPTIONS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "OPTIONS:");
println!(" --debug Run with debug profile (default)"); println!(" --debug Run with debug profile (default)");
println!(" --release Run with release profile"); println!(" --release Run with release profile");
println!(" --target <triple> Cross-compile for the given target"); println!(" --target <triple> Cross-compile for the given target");
@@ -91,18 +91,18 @@ pub fn run(args: &[String]) -> i32 {
let start_dir = match std::env::current_dir() { let start_dir = match std::env::current_dir() {
Ok(dir) => dir, Ok(dir) => dir,
Err(_) => { Err(_) => {
error("Failed to determine current directory"); error!("Failed to determine current directory");
return 1; return 1;
} }
}; };
let root = match find_project_root(&start_dir) { let root = match find_project_root(&start_dir) {
Ok(Some(dir)) => dir, Ok(Some(dir)) => dir,
Ok(None) => { Ok(None) => {
error("dcr.toml file not found"); error!("dcr.toml file not found");
return 1; return 1;
} }
Err(_) => { Err(_) => {
error("Failed to find project root"); error!("Failed to find project root");
return 1; return 1;
} }
}; };
@@ -111,7 +111,8 @@ pub fn run(args: &[String]) -> i32 {
}) { }) {
Ok(cfg) => cfg, Ok(cfg) => cfg,
Err(err) => { Err(err) => {
error(&err); error!("{err}");
return 1; return 1;
} }
}; };
@@ -127,7 +128,7 @@ pub fn run(args: &[String]) -> i32 {
let display = display_run_cmd(&cmd, &flags.bin_args); let display = display_run_cmd(&cmd, &flags.bin_args);
println!( println!(
" {} {}", " {} {}",
colored(&format!("{:<9}", "run"), BOLD_GREEN), format!("{:<9}", "run").style(BOLD_GREEN),
display display
); );
return run_shell_with_args(&cmd, &flags.bin_args); return run_shell_with_args(&cmd, &flags.bin_args);
@@ -143,11 +144,12 @@ pub fn run(args: &[String]) -> i32 {
) { ) {
Ok(Some(ws)) => ws, Ok(Some(ws)) => ws,
Ok(None) => { Ok(None) => {
error("Workspace root has no members defined"); error!("Workspace root has no members defined");
return 1; return 1;
} }
Err(e) => { Err(e) => {
error(&e); error!("{e}");
return 1; return 1;
} }
}; };
@@ -159,9 +161,9 @@ pub fn run(args: &[String]) -> i32 {
Some(m) => m, Some(m) => m,
None => { None => {
if let Some(name) = &flags.workspace { if let Some(name) = &flags.workspace {
error(&format!("Workspace member '{name}' not found")); error!("Workspace member '{name}' not found");
} else { } else {
error("No workspace member to run (set `main = true` on one member)"); error!("No workspace member to run (set `main = true` on one member)");
} }
return 1; return 1;
} }
@@ -172,7 +174,8 @@ pub fn run(args: &[String]) -> i32 {
}) { }) {
Ok(code) => code, Ok(code) => code,
Err(e) => { Err(e) => {
error(&e); error!("{e}");
1 1
} }
}; };
@@ -181,7 +184,7 @@ pub fn run(args: &[String]) -> i32 {
match run_project(&root, &flags, None) { match run_project(&root, &flags, None) {
Ok(code) => code, Ok(code) => code,
Err(e) => { Err(e) => {
error(&e); error!("{e}");
1 1
} }
} }
@@ -265,7 +268,7 @@ fn run_project(
let display = display_run_cmd(&cmd, &flags.bin_args); let display = display_run_cmd(&cmd, &flags.bin_args);
println!( println!(
" {} {}", " {} {}",
colored(&format!("{:<9}", "run"), BOLD_GREEN), format!("{:<9}", "run").style(BOLD_GREEN),
display display
); );
return Ok(run_shell_with_args(&cmd, &flags.bin_args)); return Ok(run_shell_with_args(&cmd, &flags.bin_args));
@@ -273,7 +276,7 @@ fn run_project(
let display = display_bin_run(&bin_path, &flags.bin_args); let display = display_bin_run(&bin_path, &flags.bin_args);
println!( println!(
" {} {}", " {} {}",
colored(&format!("{:<9}", "run"), BOLD_GREEN), format!("{:<9}", "run").style(BOLD_GREEN),
display display
); );
return Ok(run_binary( return Ok(run_binary(

View File

@@ -15,15 +15,16 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, printc}; use crate::utils::cli_styles::{HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST};
use crate::utils::log::sprintln;
/// Sets up DCR registries by loading the registry manager and printing the list of registries. /// Sets up DCR registries by loading the registry manager and printing the list of registries.
pub fn setup(args: &[String]) -> i32 { pub fn setup(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr setup", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr setup");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Sets up DCR registries. Downloads and indexes"); println!(" Sets up DCR registries. Downloads and indexes");
println!(" package registries for dependency resolution."); println!(" package registries for dependency resolution.");
return 0; return 0;

View File

@@ -15,6 +15,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::cli::build; use crate::cli::build;
use crate::config::{FILE_DCR_TEST_H, FILE_TEST_C, flags}; use crate::config::{FILE_DCR_TEST_H, FILE_TEST_C, flags};
use crate::core::build_config::Config; use crate::core::build_config::Config;
@@ -22,31 +24,35 @@ use crate::utils::build::{
get_config_opt, get_config_str, get_language_with_profile, get_list_with_profile, get_config_opt, get_config_str, get_language_with_profile, get_list_with_profile,
get_string_with_profile, resolve_compiler, resolve_pkg_config_flags, get_string_with_profile, resolve_compiler, resolve_pkg_config_flags,
}; };
use crate::utils::cli_styles::{
BOLD_GREEN, Colorize, ERROR_ST, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST, SKIP_ST, SUCCESS_ST,
Style,
};
use crate::utils::fs::{find_project_root, with_dir}; use crate::utils::fs::{find_project_root, with_dir};
use crate::utils::log::error; use crate::utils::log::sprintln;
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, BOLD_RED, RESET, colored, printc};
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
const BOLD_BLUE: &str = "\x1b[1m\x1b[94m";
/// Runs project tests and prints a unified testsuite report. /// Runs project tests and prints a unified testsuite report.
pub fn test(args: &[String]) -> i32 { pub fn test(args: &[String]) -> i32 {
let mut init_header = false; let mut init_header = false;
let mut profile = "debug"; let mut profile = "debug";
for arg in args { for arg in args {
if arg == "--help" { if arg == "--help" {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr test [--init] [--debug | --release]", BOLD_CYAN); sprintln!(
HELP_EXAMPLES_ST,
" dcr test [--init] [--debug | --release]"
);
println!(); println!();
printc("ALIASES:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "ALIASES:");
println!(" dcr tests"); println!(" dcr tests");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Runs project tests and prints a unified testsuite report."); println!(" Runs project tests and prints a unified testsuite report.");
println!(); println!();
printc("OPTIONS:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "OPTIONS:");
println!(" --init Create tests/dcr_test.h in current project"); println!(" --init Create tests/dcr_test.h in current project");
println!(" --debug Build and run tests with debug profile (default)"); println!(" --debug Build and run tests with debug profile (default)");
println!(" --release Build and run tests with release profile"); println!(" --release Build and run tests with release profile");
@@ -60,25 +66,25 @@ pub fn test(args: &[String]) -> i32 {
profile = arg.trim_start_matches("--"); profile = arg.trim_start_matches("--");
continue; continue;
} }
error("Unknown argument"); error!("Unknown argument");
return 1; return 1;
} }
let start_dir = match std::env::current_dir() { let start_dir = match std::env::current_dir() {
Ok(dir) => dir, Ok(dir) => dir,
Err(_) => { Err(_) => {
error("Failed to determine current directory"); error!("Failed to determine current directory");
return 1; return 1;
} }
}; };
let root = match find_project_root(&start_dir) { let root = match find_project_root(&start_dir) {
Ok(Some(dir)) => dir, Ok(Some(dir)) => dir,
Ok(None) => { Ok(None) => {
error("dcr.toml file not found"); error!("dcr.toml file not found");
return 1; return 1;
} }
Err(_) => { Err(_) => {
error("Failed to find project root"); error!("Failed to find project root");
return 1; return 1;
} }
}; };
@@ -87,7 +93,8 @@ pub fn test(args: &[String]) -> i32 {
match with_dir(&root, ensure_test_header) { match with_dir(&root, ensure_test_header) {
Ok(()) => {} Ok(()) => {}
Err(msg) => { Err(msg) => {
error(&msg); error!("{msg}");
return 1; return 1;
} }
} }
@@ -96,7 +103,7 @@ pub fn test(args: &[String]) -> i32 {
match with_dir(&root, || run_testsuite(profile)) { match with_dir(&root, || run_testsuite(profile)) {
Ok(code) => code, Ok(code) => code,
Err(msg) => { Err(msg) => {
error(&msg); error!("{msg}");
1 1
} }
} }
@@ -193,19 +200,19 @@ fn run_testsuite(profile: &str) -> Result<i32, String> {
for line in stdout.lines() { for line in stdout.lines() {
let line = line.trim(); let line = line.trim();
if let Some(name) = line.strip_prefix("[PASS] ") { if let Some(name) = line.strip_prefix("[PASS] ") {
println!("{} {}", colored("[PASS]", BOLD_GREEN), name); println!("{} {}", "[PASS]".style(SUCCESS_ST), name);
pass += 1; pass += 1;
parsed_any = true; parsed_any = true;
continue; continue;
} }
if let Some(name) = line.strip_prefix("[SKIP] ") { if let Some(name) = line.strip_prefix("[SKIP] ") {
println!("{} {}", colored("[SKIP]", BOLD_BLUE), name); println!("{} {}", "[SKIP]".style(SKIP_ST), name);
skip += 1; skip += 1;
parsed_any = true; parsed_any = true;
continue; continue;
} }
if let Some(name) = line.strip_prefix("[FAIL] ") { if let Some(name) = line.strip_prefix("[FAIL] ") {
println!("{} {}", colored("[FAIL]", BOLD_RED), name); println!("{} {}", "[FAIL]".style(ERROR_ST), name);
fail += 1; fail += 1;
parsed_any = true; parsed_any = true;
continue; continue;
@@ -213,17 +220,17 @@ fn run_testsuite(profile: &str) -> Result<i32, String> {
if let Some((status, name)) = line.split_once('\t') { if let Some((status, name)) = line.split_once('\t') {
match status { match status {
"PASS" => { "PASS" => {
println!("{} {}", colored("[PASS]", BOLD_GREEN), name); println!("{} {}", "[PASS]".style(SUCCESS_ST), name);
pass += 1; pass += 1;
parsed_any = true; parsed_any = true;
} }
"SKIP" => { "SKIP" => {
println!("{} {}", colored("[SKIP]", BOLD_BLUE), name); println!("{} {}", "[SKIP]".style(SKIP_ST), name);
skip += 1; skip += 1;
parsed_any = true; parsed_any = true;
} }
"FAIL" => { "FAIL" => {
println!("{} {}", colored("[FAIL]", BOLD_RED), name); println!("{} {}", "[FAIL]".style(ERROR_ST), name);
fail += 1; fail += 1;
parsed_any = true; parsed_any = true;
} }
@@ -235,11 +242,11 @@ fn run_testsuite(profile: &str) -> Result<i32, String> {
if !parsed_any { if !parsed_any {
if suite_success { if suite_success {
for name in &declared { for name in &declared {
println!("{} {}", colored("[PASS]", BOLD_GREEN), name); println!("{} {}", "[PASS]".style(SUCCESS_ST), name);
} }
pass = declared.len() as i32; pass = declared.len() as i32;
} else { } else {
println!("{} testsuite", colored("[FAIL]", BOLD_RED)); println!("{} testsuite", "[FAIL]".style(ERROR_ST));
fail = 1; fail = 1;
} }
} }
@@ -251,14 +258,15 @@ fn run_testsuite(profile: &str) -> Result<i32, String> {
}; };
println!(); println!();
println!("{}", colored("=====================", BOLD_GREEN)); sprintln!(BOLD_GREEN, "=====================");
println!("{}", colored(" Testsuite summary ", BOLD_GREEN)); sprintln!(BOLD_GREEN, " Testsuite summary ");
println!("{}", colored("=====================", BOLD_GREEN)); sprintln!(BOLD_GREEN, "=====================");
println!("TOTAL: {}", total); println!("TOTAL: {}", total);
print_field("PASS", pass, BOLD_GREEN); print_field("PASS", pass, SUCCESS_ST);
print_field("SKIP", skip, BOLD_BLUE); print_field("SKIP", skip, SKIP_ST);
print_field("FAIL", fail, BOLD_RED); print_field("FAIL", fail, ERROR_ST);
println!("{}", colored("=====================", BOLD_GREEN)); sprintln!(BOLD_GREEN, "=====================");
sprintln!(BOLD_GREEN, "=====================");
if fail > 0 || !suite_success { if fail > 0 || !suite_success {
return Ok(1); return Ok(1);
@@ -393,10 +401,10 @@ fn extract_test_names_path(path: &Path) -> Vec<String> {
} }
/// Prints a test result field with color coding. /// Prints a test result field with color coding.
fn print_field(label: &str, value: i32, color: &str) { fn print_field(label: &str, value: i32, style: Style) {
if value == 0 { if value == 0 {
println!("{}: {}", label, value); println!("{}: {}", label, value);
} else { } else {
println!("{}{}: {}{}", color, label, value, RESET); sprintln!(style, "{}: {}", label, value);
} }
} }

View File

@@ -15,9 +15,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::core::build_config::Config; use crate::core::build_config::Config;
use crate::utils::log::error; use crate::utils::cli_styles::{BOLD_CYAN, HELP_EXAMPLES_ST, HELP_SECTION_TITLE_ST};
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, printc}; use crate::utils::log::sprintln;
use std::collections::HashSet; use std::collections::HashSet;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use toml::Value; use toml::Value;
@@ -28,10 +30,10 @@ use toml::Value;
/// Otherwise, it parses the dcr.toml configuration and prints the dependency tree. /// Otherwise, it parses the dcr.toml configuration and prints the dependency tree.
pub fn tree(args: &[String]) -> i32 { pub fn tree(args: &[String]) -> i32 {
if args.first().is_some_and(|a| a == "--help") { if args.first().is_some_and(|a| a == "--help") {
printc("USAGE:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "USAGE:");
printc(" dcr tree", BOLD_CYAN); sprintln!(HELP_EXAMPLES_ST, " dcr tree");
println!(); println!();
printc("DESCRIPTION:", BOLD_GREEN); sprintln!(HELP_SECTION_TITLE_ST, "DESCRIPTION:");
println!(" Displays the dependency tree of the current project."); println!(" Displays the dependency tree of the current project.");
return 0; return 0;
} }
@@ -40,7 +42,7 @@ pub fn tree(args: &[String]) -> i32 {
let config = match Config::open("dcr.toml") { let config = match Config::open("dcr.toml") {
Ok(c) => c, Ok(c) => c,
Err(_) => { Err(_) => {
error("dcr.toml not found in current directory"); error!("dcr.toml not found in current directory");
return 1; return 1;
} }
}; };
@@ -54,7 +56,7 @@ pub fn tree(args: &[String]) -> i32 {
.and_then(|v| v.as_str()) .and_then(|v| v.as_str())
.unwrap_or("0.0.0"); .unwrap_or("0.0.0");
printc(&format!("{} v{}", name, version), BOLD_CYAN); sprintln!(BOLD_CYAN, "{} v{}", name, version);
let mut seen = HashSet::new(); let mut seen = HashSet::new();
seen.insert(name.to_string()); seen.insert(name.to_string());

View File

@@ -15,8 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::core::build::builder::BuildContext; use crate::core::build::{builder::BuildContext, common};
use crate::core::build::common;
use crate::platform; use crate::platform;
use crate::utils::build::is_bare_metal_target; use crate::utils::build::is_bare_metal_target;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View File

@@ -15,9 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::core::build::builder::BuildContext; use crate::core::build::{builder::BuildContext, builder::artifact, common};
use crate::core::build::builder::artifact;
use crate::core::build::common;
use crate::utils::build::{is_bare_metal_target, is_compile_only, is_flat_bin}; use crate::utils::build::{is_bare_metal_target, is_compile_only, is_flat_bin};
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View File

@@ -20,15 +20,10 @@
/// This module provides the MSVC-specific build implementation for C and C++ /// This module provides the MSVC-specific build implementation for C and C++
/// projects, including object compilation and linking for executables, static /// projects, including object compilation and linking for executables, static
/// libraries, shared libraries, and flat binaries. /// libraries, shared libraries, and flat binaries.
use crate::core::build::builder::BuildContext; use crate::core::build::{builder::BuildContext, builder::artifact, common};
use crate::core::build::builder::artifact;
use crate::core::build::common;
use crate::platform; use crate::platform;
use crate::utils::build::{is_compile_only, is_flat_bin}; use crate::utils::build::{is_compile_only, is_flat_bin};
use std::fs; use std::{fs, path::Path, process::Command, time::Instant};
use std::path::Path;
use std::process::Command;
use std::time::Instant;
/// Builds the project target using the MSVC compiler. /// Builds the project target using the MSVC compiler.
pub fn build(ctx: &BuildContext) -> Result<f64, String> { pub fn build(ctx: &BuildContext) -> Result<f64, String> {

View File

@@ -15,8 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::core::build::builder::BuildContext; use crate::core::build::{builder::BuildContext, common};
use crate::core::build::common;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View File

@@ -1,9 +1,6 @@
use crate::core::build::builder::BuildContext; use crate::core::build::{builder::BuildContext, builder::artifact, common};
use crate::core::build::builder::artifact;
use crate::core::build::common;
use crate::utils::build::{is_compile_only, is_flat_bin}; use crate::utils::build::{is_compile_only, is_flat_bin};
use std::path::Path; use std::{path::Path, time::Instant};
use std::time::Instant;
/// Keeps only assembler-relevant flags from a cflags list. /// Keeps only assembler-relevant flags from a cflags list.
/// ///

View File

@@ -1,9 +1,6 @@
use crate::core::build::builder::BuildContext;
use crate::core::build::common;
use crate::core::build::language::asm::common as asm; use crate::core::build::language::asm::common as asm;
use std::fs; use crate::core::build::{builder::BuildContext, common};
use std::path::Path; use std::{fs, path::Path, process::Command};
use std::process::Command;
/// Builds an assembly file using the FASM assembler. /// Builds an assembly file using the FASM assembler.
/// ///

View File

@@ -15,12 +15,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::core::build::builder::BuildContext;
use crate::core::build::common;
use crate::core::build::language::asm::common as asm; use crate::core::build::language::asm::common as asm;
use std::fs; use crate::core::build::{builder::BuildContext, common};
use std::path::Path; use std::{fs, path::Path, process::Command};
use std::process::Command;
/// Builds GAS (`.s`) sources via the GNU Assembler. /// Builds GAS (`.s`) sources via the GNU Assembler.
/// ///

View File

@@ -1,9 +1,6 @@
use crate::core::build::builder::BuildContext;
use crate::core::build::common;
use crate::core::build::language::asm::common as asm; use crate::core::build::language::asm::common as asm;
use std::fs; use crate::core::build::{builder::BuildContext, common};
use std::path::Path; use std::{fs, path::Path, process::Command};
use std::process::Command;
/// Builds MASM (`.asm`) sources via `ml`. /// Builds MASM (`.asm`) sources via `ml`.
/// ///

View File

@@ -16,12 +16,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Support for NASM assembly language in the DCR build system. /// Support for NASM assembly language in the DCR build system.
use crate::core::build::builder::BuildContext;
use crate::core::build::common;
use crate::core::build::language::asm::common as asm; use crate::core::build::language::asm::common as asm;
use std::fs; use crate::core::build::{builder::BuildContext, common};
use std::path::Path; use std::{fs, path::Path, process::Command};
use std::process::Command;
/// Builds assembly sources using the NASM assembler. /// Builds assembly sources using the NASM assembler.
/// ///

View File

@@ -15,8 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::core::build::builder::BuildContext; use crate::core::{build::builder::BuildContext, build::common};
use crate::core::build::common;
use crate::utils::build::run_pkg_config; use crate::utils::build::run_pkg_config;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View File

@@ -1,10 +1,6 @@
use crate::core::build::builder::BuildContext;
use crate::core::build::common;
use crate::core::build::language::Language;
use crate::core::build::language::asm::common as asm; use crate::core::build::language::asm::common as asm;
use std::fs; use crate::core::build::{builder::BuildContext, common, language::Language};
use std::path::Path; use std::{fs, path::Path, process::Command};
use std::process::Command;
/// Represents the LLVM IR language implementation for the build system. /// Represents the LLVM IR language implementation for the build system.
pub struct LlvmIr; pub struct LlvmIr;

View File

@@ -15,8 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::core::build::engine::ToolchainExecs; use crate::core::{build::engine::ToolchainExecs, build_config::Config};
use crate::core::build_config::Config;
use crate::utils::build::{VersionInfo, profile_table, substitute_vars}; use crate::utils::build::{VersionInfo, profile_table, substitute_vars};
use glob::glob; use glob::glob;
use std::fs; use std::fs;

View File

@@ -15,12 +15,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use serde::Deserialize; use serde::Deserialize;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use toml::Value; use toml::{Value, map::Map};
use toml::map::Map;
use toml_edit::{DocumentMut, Item, Table}; use toml_edit::{DocumentMut, Item, Table};
const DEFAULT_VERSION: &str = "0.1.0"; const DEFAULT_VERSION: &str = "0.1.0";
@@ -334,35 +335,35 @@ impl Config {
.map(str::trim) .map(str::trim)
.filter(|s| !s.is_empty()); .filter(|s| !s.is_empty());
let Some(required) = required else { let Some(required) = required else {
crate::utils::log::warn(&format!( warn!(
"package.dcr-version is missing in {}. Add e.g. dcr-version = \"{tool}\" \ "package.dcr-version is missing in {}. Add e.g. dcr-version = \"{tool}\" \
so collaborators know which dcr this project targets \ so collaborators know which dcr this project targets \
(written automatically by `dcr new` / `dcr init`).", (written automatically by `dcr new` / `dcr init`).",
self.path.display() self.path.display(),
)); );
return; return;
}; };
match crate::utils::build::compare_semver(tool, required) { match crate::utils::build::compare_semver(tool, required) {
Some(std::cmp::Ordering::Less) => { Some(std::cmp::Ordering::Less) => {
crate::utils::log::warn(&format!( warn!(
"this project requires dcr {required} (package.dcr-version), \ "this project requires dcr {required} (package.dcr-version), \
but you are running dcr {tool}. Some features may be missing or misbehave. \ but you are running dcr {tool}. Some features may be missing or misbehave. \
Upgrade dcr (e.g. `dcr --update`) or lower package.dcr-version if intentional." Upgrade dcr (e.g. `dcr --update`) or lower package.dcr-version if intentional."
)); );
} }
Some(std::cmp::Ordering::Greater) => { Some(std::cmp::Ordering::Greater) => {
crate::utils::log::warn(&format!( warn!(
"this project pins dcr {required} (package.dcr-version), \ "this project pins dcr {required} (package.dcr-version), \
but you are running newer dcr {tool}. Config/defaults may be outdated. \ but you are running newer dcr {tool}. Config/defaults may be outdated. \
Review the changelog and bump package.dcr-version when the project is verified." Review the changelog and bump package.dcr-version when the project is verified.",
)); );
} }
Some(std::cmp::Ordering::Equal) => {} Some(std::cmp::Ordering::Equal) => {}
None => { None => {
crate::utils::log::warn(&format!( warn!(
"package.dcr-version = \"{required}\" is not a valid semver (expected X.Y.Z); ignoring" "package.dcr-version = \"{required}\" is not a valid semver (expected X.Y.Z); ignoring",
)); );
} }
} }
} }

View File

@@ -15,8 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::fs; use std::{fs, path::Path};
use std::path::Path;
/// Represents a locked dependency entry containing its name, version, source, and checksum. /// Represents a locked dependency entry containing its name, version, source, and checksum.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@@ -17,9 +17,7 @@
/// Registry configuration management for DCR. /// Registry configuration management for DCR.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::{collections::HashMap, fs, path::PathBuf};
use std::fs;
use std::path::PathBuf;
/// Represents a registry configuration entry with its URL and priority. /// Represents a registry configuration entry with its URL and priority.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]

View File

@@ -16,8 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::platform; use crate::platform;
use std::path::Path; use std::{path::Path, process::Command};
use std::process::Command;
/// Runs the built binary for a project, forwarding `bin_args`. /// Runs the built binary for a project, forwarding `bin_args`.
/// ///

View File

@@ -15,8 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::fs; use std::{fs, path::Path};
use std::path::Path;
/// Initializes a Git repository in the target directory and configures its `.gitignore` file. /// Initializes a Git repository in the target directory and configures its `.gitignore` file.
/// ///

View File

@@ -14,19 +14,23 @@
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::utils::log::error;
mod cli; mod cli;
mod config; mod config;
mod core; mod core;
mod platform; mod platform;
mod prelude;
mod utils; mod utils;
use prelude::*;
/// Main entry point for DCR, a Cargo-like C/C++ project manager. /// Main entry point for DCR, a Cargo-like C/C++ project manager.
/// ///
/// Collects command-line arguments via `std::env::args()` and dispatches to the /// Collects command-line arguments via `std::env::args()` and dispatches to the
/// appropriate subcommand handler. With fewer than two arguments, shows help and /// appropriate subcommand handler. With fewer than two arguments, shows help and
/// exits with code 0; otherwise exits with the handler's exit code. /// exits with code 0; otherwise exits with the handler's exit code.
fn main() { fn main() {
utils::log::init();
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();
if args.len() < 2 { if args.len() < 2 {
@@ -61,7 +65,7 @@ fn main() {
"--help" => cli::help::help(), "--help" => cli::help::help(),
"--update" => cli::flag_update::flag_update(rest), "--update" => cli::flag_update::flag_update(rest),
_ => { _ => {
error("Unknown command or argument"); error!("Unknown command or argument");
1 1
} }
}; };

1
src/prelude.rs Normal file
View File

@@ -0,0 +1 @@
pub use log::{error, warn};

View File

@@ -15,8 +15,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::prelude::*;
use crate::core::build_config::Config; use crate::core::build_config::Config;
use crate::utils::log::warn;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
/// Version information parsed from a version string. /// Version information parsed from a version string.
@@ -251,10 +252,9 @@ pub fn normalize_target_os(target: &str) -> &str {
"windows" => "x86_64-pc-windows-msvc", "windows" => "x86_64-pc-windows-msvc",
_ if target.contains('-') => target, _ if target.contains('-') => target,
_ => { _ => {
warn(&format!( warn!(
"Unknown target '{}', using as-is. Supported short names: linux, macos, windows", "Unknown target '{target}', using as-is. Supported short names: linux, macos, windows"
target );
));
target target
} }
} }

View File

@@ -15,39 +15,33 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#[allow(dead_code)] pub use owo_colors::{OwoColorize as Colorize, Style};
pub const RESET: &str = "\x1b[0m";
// Сli theme styles
// Main
#[allow(dead_code)] #[allow(dead_code)]
pub const BOLD: &str = "\x1b[1m"; pub const BOLD_RED: Style = Style::new().bright_red().bold();
#[allow(dead_code)]
pub const BOLD_GREEN: Style = Style::new().bright_green().bold();
#[allow(dead_code)]
pub const BOLD_YELLOW: Style = Style::new().bright_yellow().bold();
#[allow(dead_code)]
pub const BOLD_CYAN: Style = Style::new().bright_cyan().bold();
#[allow(dead_code)]
pub const BOLD_BLUE: Style = Style::new().bright_blue().bold();
// Help
#[allow(dead_code)] #[allow(dead_code)]
pub const BRIGHT_RED: &str = "\x1b[91m"; pub const HELP_SECTION_TITLE_ST: Style = BOLD_GREEN;
#[allow(dead_code)] #[allow(dead_code)]
pub const BRIGHT_GREEN: &str = "\x1b[92m"; pub const HELP_EXAMPLES_ST: Style = BOLD_CYAN;
#[allow(dead_code)]
pub const BRIGHT_YELLOW: &str = "\x1b[93m";
#[allow(dead_code)]
pub const BRIGHT_CYAN: &str = "\x1b[96m";
#[allow(dead_code)]
pub const BOLD_RED: &str = "\x1b[1m\x1b[91m";
#[allow(dead_code)]
pub const BOLD_GREEN: &str = "\x1b[1m\x1b[92m";
#[allow(dead_code)]
pub const BOLD_YELLOW: &str = "\x1b[1m\x1b[93m";
#[allow(dead_code)]
pub const BOLD_CYAN: &str = "\x1b[1m\x1b[96m";
/// Applies ANSI escape codes to format the message with the given style. // Other
///
/// This is a utility for colored output in terminal applications.
#[allow(dead_code)] #[allow(dead_code)]
pub fn colored(msg: &str, style: &str) -> String { pub const ERROR_ST: Style = BOLD_RED;
format!("{style}{msg}{RESET}")
}
/// Prints `msg` to stdout with the given ANSI `style`.
#[allow(dead_code)] #[allow(dead_code)]
pub fn printc(msg: &str, style: &str) { pub const SUCCESS_ST: Style = BOLD_GREEN;
println!("{style}{msg}{RESET}"); #[allow(dead_code)]
} pub const ALERT_ST: Style = BOLD_YELLOW;
#[allow(dead_code)]
pub const SKIP_ST: Style = BOLD_BLUE;

View File

@@ -1,5 +1,4 @@
use std::path::Path; use std::{path::Path, process::Command};
use std::process::Command;
/// Checks if the `git` executable is available in the system's PATH. /// Checks if the `git` executable is available in the system's PATH.
/// ///

View File

@@ -15,17 +15,55 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::utils::text::{BOLD_RED, BOLD_YELLOW, colored}; use env_logger::{Builder, WriteStyle};
use log::{Level, LevelFilter};
use owo_colors::OwoColorize;
use std::io::Write;
/// Logs an error message to the standard error stream. // A function for initializing the logging lib with basic settings.
/// pub fn init() {
/// The message is prefixed with a red "error" label for visibility. Builder::new()
#[allow(dead_code)] .filter_level(LevelFilter::Warn)
pub fn error(msg: &str) { .write_style(WriteStyle::Auto)
eprintln!("{}: {msg}", colored("error", BOLD_RED)); .format(|buf, record| {
let colored_lv = match record.level() {
Level::Error => record.level().red().to_string(),
Level::Warn => record.level().yellow().to_string(),
Level::Info => record.level().default_color().to_string(),
_ => record.level().purple().italic().to_string(),
};
writeln!(
buf,
"{}: {}",
colored_lv.to_lowercase().bold(),
record.args(),
)
})
.init();
} }
#[allow(dead_code)] /// Prints message to stdout with the given `Style`. Copy of println! macro
pub fn warn(msg: &str) { #[allow(unused_macros)]
eprintln!("{}: {msg}", colored("warn", BOLD_YELLOW)); macro_rules! sprintln {
($style:expr, $($arg:tt)*) => {
{
use ::owo_colors::OwoColorize as _;
println!("{}", format!($($arg)*).style($style));
}
};
} }
/// Prints message to stdout with the given `Style`. Copy of print! macro
#[allow(unused_macros)]
macro_rules! sprint {
($style:expr, $($arg:tt)*) => {
{
use ::owo_colors::OwoColorize as _;
print!("{}", format!($($arg)*).style($style));
}
};
}
#[allow(unused_imports)]
pub(crate) use {sprint, sprintln};

View File

@@ -29,4 +29,4 @@ pub mod git;
pub mod log; pub mod log;
/// Text processing and manipulation helpers. /// Text processing and manipulation helpers.
pub mod text; pub mod cli_styles;