diff --git a/AGENT.md b/AGENT.md deleted file mode 100644 index fbec080..0000000 --- a/AGENT.md +++ /dev/null @@ -1,77 +0,0 @@ -# AGENT.md - -## Project Overview - -**DCR (Dexoron Cargo Realization)** — CLI tool for managing C/C++/ASM projects in a Cargo-style. - -- **Implementation Language**: Rust (edition 2024) -- **License**: MIT -- **Repository**: [github.com/dexoron/dcr](https://github.com/dexoron/dcr) - -## Architecture - -### Source Tree (`src/`) - -``` -src/ -├── main.rs # Entry point, CLI command routing -├── config.rs # Global constants -├── cli/ # CLI command implementation -│ ├── new.rs # dcr new -│ ├── init.rs # dcr init -│ ├── setup.rs # dcr setup (registry initialization) -│ ├── add.rs # dcr add -│ ├── build.rs # dcr build -│ ├── run.rs # dcr run -│ ├── clean.rs # dcr clean -│ ├── help.rs # dcr --help -│ └── flag_update.rs # dcr --update -├── core/ # Business logic -│ ├── config.rs # dcr.toml parsing -│ ├── registry.rs # Package index management (RegistryManager) -│ ├── deps/ # Dependency resolution (path, git) -│ ├── runner.rs # Binary execution -│ ├── workspace.rs # [workspace] support -│ └── builder/ # Compiler backends (gcc, clang, msvc, nasm, gas) -├── platform/ # Platform-specific logic -└── utils/ # Helper utilities -``` - -### Key Concepts - -- **`~/.dcr/`**: Centralized storage for registries and configurations. -- **`config.toml`**: File in `~/.dcr/` defining available package indices and their priorities. -- **`dcr.toml`**: Local project configuration. -- **Registry System**: `RegistryManager` handles package discovery across multiple indices based on priority. - -## Supported Platforms - -| Platform | Target triple | -|---|---| -| Linux | `x86_64-unknown-linux-gnu` | -| macOS Intel | `x86_64-apple-darwin` | -| macOS ARM | `aarch64-apple-darwin` | -| Windows | `x86_64-pc-windows-msvc` | - -## Dependencies (Cargo) - -| Crate | Purpose | -|---|---| -| `reqwest` | HTTP requests | -| `serde` | Serialization | -| `toml` | Configuration parsing | -| `git2` | Git dependency handling | - -## Development - -### Build & Run -```bash -cargo build --release -./target/release/dcr setup -``` - -## Code Conventions -- Rust stable edition 2024. -- `cargo fmt` and `cargo clippy -- -D warnings`. -- CLI output in English. -- Use `utils::log` for messaging. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..63afa31 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,56 @@ +# AGENTS.md + +## Quick start + +```bash +cargo build --release && ./target/release/dcr setup +``` + +## Pre-PR (CI) gate + +```bash +cargo fmt --all --check +cargo clippy --all-targets --all-features -- -D warnings +cargo check --all-targets --all-features +cargo test +``` + +CI (`.github/workflows/ci.yml`) runs fmt, clippy, check — **not** tests. + +## Tests + +- Integration tests in `tests/` build the dcr binary and run it in temp dirs under `sandbox/cli-tests/` (gitignored). +- Some tests **skip** if no C compiler (`gcc`/`clang`/`cc`) is found at runtime. +- Compiler can be overridden via `DCR_COMPILER` or `DCR_CC` env vars. +- Tests use `$CARGO_BIN_EXE_dcr` when available, else `target/debug/dcr`. + +## Architecture + +- **Single Rust package** (edition 2024), not a workspace. Only one binary: `dcr`. +- **Entrypoint**: `src/main.rs` — matches `args[1]` against command names. +- **Compiler backends** in `src/core/builder/`: `unix_cc` (gcc/clang), `msvc` (cl/clang-cl), `gas`, `nasm`. Dispatched by matching compiler name string. +- **Config**: `dcr.toml` (per-project), `~/.dcr/config.toml` (registry), `~/.dcr/index.json` (index). Defaults: `language=c`, `standard=c11`, `compiler=clang`, `kind=bin`. +- **`build.rs`** embeds `TARGET` as `DCR_TARGET` env var (shown in `--version`). +- **CLI output** must be in English. Use `utils::log` for messaging. + +## Key commands + +| Command | Notes | +|---|---| +| `dcr new ` | Creates project dir | +| `dcr init` | Init from CWD (must be empty) | +| `dcr build [--debug\|--release] [--target ]` | Default profile: `--debug` | +| `dcr run [--debug\|--release]` | Only for `kind = "bin"` | +| `dcr clean [--debug\|--release] [--all]` | `--all` for workspace roots | +| `dcr tree` | Dependency tree viewer | +| `dcr test [--init]` | Runs/test project tests | +| `dcr gen ` | IDE integration | +| `dcr --update` | Self-update from GitHub Releases | + +## Cross-compilation + +`--target` accepts short names (`linux`, `macos`, `windows`) or full triples. In CI, Linux cross-builds use `cargo-zigbuild`. When `build.target` is set in `dcr.toml`, DCR auto-injects `--target=` into cflags (clang). Bare-metal targets (containing `none`/`-elf`/`eabi`/`baremetal`) skip DCR's default compiler flags. + +## Dependencies (Cargo) + +reqwest (blocking+json+rustls-tls), serde, toml, git2, openssl (vendored), self-replace, sha2, ctrlc. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eebbea..f34f3e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ Added: Produces `KERNEL.EFI` (works for `bin`, `staticlib`, and `sharedlib`). - Automatic injection of `--target=` into compiler flags when `build.target` is set in `dcr.toml`. This greatly simplifies clang-based cross-compilation (especially for bare-metal targets like `aarch64-none-elf`). - Bare-metal targets (containing `none`, `-elf`, `eabi`, `baremetal`) no longer receive DCR's internal default flags (`-g`, `-Wall`, `-Wextra`, `-fno-omit-frame-pointer`, `-DDCR_DEBUG`, etc.). Prevents unwanted sections (`.comment`, debug info, etc.) that break custom linker scripts when `inherit = true`. +- `build.ldscript` in `dcr.toml` — linker script path passed as `-T ` to the linker. Essential for bare-metal/embedded/freestanding targets. +- `dcr build --verbose` — prints compiler/linker command lines (also works with `DCR_DEBUG` env var). +- `dcr add ` without a source argument — if a registry is configured, DCR looks up the package by name and adds it as a version dependency. Fixed: diff --git a/docs/commands/dcr-add.md b/docs/commands/dcr-add.md index 356ac71..701184f 100644 --- a/docs/commands/dcr-add.md +++ b/docs/commands/dcr-add.md @@ -5,11 +5,15 @@ Add a dependency to `dcr.toml`. ## Usage ```bash -dcr add [options] +dcr add [source] [options] ``` +If `` is omitted and a registry is configured, DCR will look up the +package by name in the registry and add it as a version dependency. + ## Sources +- *omitted*: Look up in the configured registry. - `path:./path/to/lib`: Local directory. - `github:user/repo`: GitHub repository. - `gitlab:user/repo`: GitLab repository. @@ -29,4 +33,5 @@ dcr add my_lib path:./libs/my_lib dcr add cool_lib github:user/cool_lib dcr add shared_lib gitlab:org/shared --branch develop dcr add custom_lib git:git.internal.com/r/lib --tag v1.0.0 +dcr add mylib # Looks up in registry ``` diff --git a/docs/commands/dcr-build.md b/docs/commands/dcr-build.md index dd08931..d712c26 100644 --- a/docs/commands/dcr-build.md +++ b/docs/commands/dcr-build.md @@ -35,6 +35,7 @@ dcr build --debug --clean - `build.platform` - `build.cflags` - `build.ldflags` +- `build.ldscript` - `build.debug` / `build.release` - `build.exclude` - `build.include` @@ -58,6 +59,7 @@ dcr build --debug --clean - Profile flag (`--debug` / `--release`) can appear in any argument position (duplicates are rejected). - Unknown profile flags return an error. - `--target ` builds for specified target (short names: `linux`, `macos`, `windows`). +- `--verbose` prints the compiler/linker command lines to stderr (also works via `DCR_DEBUG` env var). - If no `--target`, builds for `build.targets` or native. - Incremental rebuild checks source/object mtime and tracked header dependencies. - `--force` skips build cache checks and recompiles. diff --git a/docs/configuration/build-section.md b/docs/configuration/build-section.md index ac59089..a556406 100644 --- a/docs/configuration/build-section.md +++ b/docs/configuration/build-section.md @@ -43,6 +43,7 @@ kind = "bin" - `platform` (string, optional): architecture hint for compiler (used for `-march` or `/arch`). - `cflags` (string array, optional): extra compile flags. - `ldflags` (string array, optional): extra link flags. +- `ldscript` (string, optional): linker script path. Passed as `-T ` to the linker. Useful for bare-metal/embedded/freestanding targets. - `exclude` (string array, optional): paths or glob patterns to skip during source/header collection. - `include` (string array, optional): allowlist paths or globs that override `exclude`. - `include` directory entries (non-glob) are also passed to the compiler as `-I` include paths. diff --git a/docs/reference-and-help/cli-reference.md b/docs/reference-and-help/cli-reference.md index 5aab80e..43d0449 100644 --- a/docs/reference-and-help/cli-reference.md +++ b/docs/reference-and-help/cli-reference.md @@ -20,7 +20,7 @@ dcr --update ## Notes on argument parsing -- `build`/`run` parse flags in any order (`--debug|--release`, `--force`, `--clean`) and reject duplicates. +- `build`/`run` parse flags in any order (`--debug|--release`, `--force`, `--clean`, `--verbose`) and reject duplicates. - `new` requires exactly one argument. - `init` and `--update` do not accept extra arguments. - `clean` accepts `--debug|--release` and optional `--all`. diff --git a/docs/reference-and-help/config-reference.md b/docs/reference-and-help/config-reference.md index b62110e..231a50e 100644 --- a/docs/reference-and-help/config-reference.md +++ b/docs/reference-and-help/config-reference.md @@ -18,6 +18,7 @@ target = "string" # optional platform = "string" # optional cflags = ["string", "..."] # optional ldflags = ["string", "..."] # optional +ldscript = "string" # optional (linker script, passed as -T) exclude = ["string", "..."] # optional include = ["string", "..."] # optional roots = ["string", "..."] # optional diff --git a/src/cli/add.rs b/src/cli/add.rs index afcdcce..ad499a1 100644 --- a/src/cli/add.rs +++ b/src/cli/add.rs @@ -1,4 +1,5 @@ use crate::core::config::Config; +use crate::core::deps::register; use crate::utils::fs::find_project_root; use crate::utils::log::error; use crate::utils::text::{BOLD_GREEN, colored}; @@ -12,6 +13,7 @@ pub struct AddArgs { pub branch: Option, pub tag: Option, pub rev: Option, + pub version_from_registry: Option, } pub fn add(args: &[String]) -> i32 { @@ -71,6 +73,8 @@ pub fn add(args: &[String]) -> i32 { let mut table = Map::new(); table.insert("path".to_string(), Value::String(path)); Value::Table(table) + } else if let Some(version) = add_args.version_from_registry { + Value::String(version) } else { error("Dependency source (path or git) must be provided"); return 1; @@ -124,8 +128,38 @@ fn parse_add_args(args: &[String]) -> Result { } source_spec = s.clone(); } else { - error("Dependency source is required"); - return Err(1); + // No source provided — try registry auto-lookup + match register::resolve_package_from_registry(&name) { + Ok(info) => { + let version = info + .get("latest_version") + .or_else(|| info.get("version")) + .and_then(|v| v.as_str()) + .ok_or_else(|| { + ( + "Registry entry for `".to_string() + &name + "` has no version field", + 1, + ) + }) + .map_err(|(msg, code)| { + error(&msg); + code + })?; + return Ok(AddArgs { + name, + path: None, + git: None, + branch: None, + tag: None, + rev: None, + version_from_registry: Some(version.to_string()), + }); + } + Err(e) => { + error(&format!("Cannot add `{}` without source: {}", name, e)); + return Err(1); + } + } } while let Some(arg) = iter.next() { @@ -197,5 +231,6 @@ fn parse_add_args(args: &[String]) -> Result { branch, tag, rev, + version_from_registry: None, }) } diff --git a/src/cli/build.rs b/src/cli/build.rs index 6039c99..3a1f23c 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -77,7 +77,13 @@ pub fn build(args: &[String]) -> i32 { let _ = clean(&clean_args); } match with_dir(&root, || { - build_from_root(&root, &flags.profile, flags.target.as_deref(), flags.force) + build_from_root( + &root, + &flags.profile, + flags.target.as_deref(), + flags.force, + flags.verbose, + ) }) { Ok(()) => 0, Err(msg) => { @@ -377,6 +383,7 @@ fn build_from_root( profile: &str, target: Option<&str>, force: bool, + verbose: bool, ) -> Result<(), String> { let config = Config::open("./dcr.toml").map_err(|err| err.to_string())?; let project_name = get_config_str(&config, "package.name"); @@ -418,12 +425,19 @@ fn build_from_root( ); } if let Some(workspace) = parse_workspace(&config, profile, build_target.as_deref(), root)? { - build_workspace(&workspace, profile, build_target.as_deref(), force)?; + build_workspace(&workspace, profile, build_target.as_deref(), force, verbose)?; let excludes: Vec = workspace.members.iter().map(|m| m.path.clone()).collect(); - build_project_at(root, profile, build_target.as_deref(), &excludes, force)?; + build_project_at( + root, + profile, + build_target.as_deref(), + &excludes, + force, + verbose, + )?; } else { - build_project_at(root, profile, build_target.as_deref(), &[], force)?; + build_project_at(root, profile, build_target.as_deref(), &[], force, verbose)?; } } let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0; @@ -440,9 +454,10 @@ fn build_workspace( profile: &str, target: Option<&str>, force: bool, + verbose: bool, ) -> Result<(), String> { for member in &workspace.members { - build_project_at(&member.path, profile, target, &[], force)?; + build_project_at(&member.path, profile, target, &[], force, verbose)?; } Ok(()) } @@ -453,6 +468,7 @@ fn build_project_at( target: Option<&str>, exclude_dirs: &[std::path::PathBuf], force: bool, + verbose: bool, ) -> Result<(), String> { with_dir(project_root, || { check_interrupted()?; @@ -500,21 +516,30 @@ fn build_project_at( .or_else(|| get_config_opt(&config, "toolchain.rcc")); let mut build_cflags = get_list_with_profile_and_target(&config, "cflags", profile, build_target)?; - // Автоматически добавляем --target=..., если задан build.target и флаг ещё не указан - if let Some(t) = build_target { - if !t.trim().is_empty() { - let target_flag = format!("--target={}", t.trim()); - if !build_cflags.iter().any(|f| f == &target_flag || f.starts_with("--target=")) { - build_cflags.insert(0, target_flag); - } + if let Some(t) = build_target + && !t.trim().is_empty() + { + let target_flag = format!("--target={}", t.trim()); + if !build_cflags + .iter() + .any(|f| f == &target_flag || f.starts_with("--target=")) + { + build_cflags.insert(0, target_flag); } } - let build_ldflags = + let mut build_ldflags = get_list_with_profile_and_target(&config, "ldflags", profile, build_target)?; + let build_ldscript = + get_string_with_profile_and_target(&config, "ldscript", profile, build_target); + if !build_ldscript.is_empty() { + build_ldflags.push(format!("-T{}", build_ldscript)); + } // Новые поля: filename и extension - let output_filename = get_string_with_profile_and_target(&config, "filename", profile, build_target); - let output_extension = get_string_with_profile_and_target(&config, "extension", profile, build_target); + let output_filename = + get_string_with_profile_and_target(&config, "filename", profile, build_target); + let output_extension = + get_string_with_profile_and_target(&config, "extension", profile, build_target); let build_excludes = get_list_with_profile_and_target(&config, "exclude", profile, build_target)?; let build_includes = @@ -578,7 +603,7 @@ fn build_project_at( dep_root.display() )); } - build_project_at(&dep_root, profile, build_target, &[], force)?; + build_project_at(&dep_root, profile, build_target, &[], force, verbose)?; print!( "\r{:100}\r {} {} v{}", "", @@ -693,8 +718,17 @@ fn build_project_at( libs: &resolved.libs, cflags: &resolved_cflags, ldflags: &resolved_ldflags, - output_filename: if output_filename.is_empty() { None } else { Some(output_filename.as_str()) }, - output_extension: if output_extension.is_empty() { None } else { Some(output_extension.as_str()) }, + output_filename: if output_filename.is_empty() { + None + } else { + Some(output_filename.as_str()) + }, + output_extension: if output_extension.is_empty() { + None + } else { + Some(output_extension.as_str()) + }, + verbose, }; if std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] debug: compiler={}", ctx.compiler); diff --git a/src/cli/flags.rs b/src/cli/flags.rs index 23ab6e8..59f959e 100644 --- a/src/cli/flags.rs +++ b/src/cli/flags.rs @@ -6,6 +6,7 @@ pub struct BuildRunFlags { pub target: Option, pub force: bool, pub clean: bool, + pub verbose: bool, } pub fn parse_build_run_flags(args: &[String]) -> Result { @@ -13,6 +14,7 @@ pub fn parse_build_run_flags(args: &[String]) -> Result { let mut target = None; let mut force = false; let mut clean = false; + let mut verbose = false; let mut iter = args.iter(); while let Some(arg) = iter.next() { @@ -29,6 +31,10 @@ pub fn parse_build_run_flags(args: &[String]) -> Result { clean = true; continue; } + if candidate == "verbose" { + verbose = true; + continue; + } if candidate == "target" { if let Some(t) = iter.next() { target = Some(t.clone()); @@ -55,5 +61,6 @@ pub fn parse_build_run_flags(args: &[String]) -> Result { target, force, clean, + verbose, }) } diff --git a/src/cli/gen.rs b/src/cli/gen.rs index e1c70b7..1f27cf8 100644 --- a/src/cli/gen.rs +++ b/src/cli/gen.rs @@ -339,6 +339,7 @@ fn collect_project_info_inner(root: &Path, profile: &str) -> Result Result { for obj in &objects { cmd.arg(obj); } - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } match cmd.status() { @@ -72,7 +72,11 @@ pub fn build(ctx: &BuildContext) -> Result { } let name = ctx.output_filename.unwrap_or(ctx.project_name); let ext = ctx.output_extension.unwrap_or(""); - let final_name = if ext.is_empty() { name.to_string() } else { format!("{}.{}", name, ext) }; + let final_name = if ext.is_empty() { + name.to_string() + } else { + format!("{}.{}", name, ext) + }; let out_path = if ctx.kind == "sharedlib" { platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir) @@ -81,7 +85,7 @@ pub fn build(ctx: &BuildContext) -> Result { }; cmd.arg("-o").arg(out_path); - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } match cmd.status() { diff --git a/src/core/builder/mod.rs b/src/core/builder/mod.rs index 26d015b..fa7e446 100644 --- a/src/core/builder/mod.rs +++ b/src/core/builder/mod.rs @@ -28,6 +28,7 @@ pub struct BuildContext<'a> { pub ldflags: &'a [String], pub output_filename: Option<&'a str>, pub output_extension: Option<&'a str>, + pub verbose: bool, } pub fn build(ctx: &BuildContext) -> Result { diff --git a/src/core/builder/msvc.rs b/src/core/builder/msvc.rs index a073f09..49b3c96 100644 --- a/src/core/builder/msvc.rs +++ b/src/core/builder/msvc.rs @@ -28,7 +28,7 @@ pub fn build(ctx: &BuildContext) -> Result { for obj in &objects { cmd.arg(obj); } - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } match cmd.status() { @@ -97,7 +97,7 @@ pub fn build(ctx: &BuildContext) -> Result { }; cmd.arg(format!("/Fe:{out_path}")); - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } match cmd.status() { diff --git a/src/core/builder/nasm.rs b/src/core/builder/nasm.rs index 0a4a43c..a44f8c9 100644 --- a/src/core/builder/nasm.rs +++ b/src/core/builder/nasm.rs @@ -37,7 +37,7 @@ pub fn build(ctx: &BuildContext) -> Result { for obj in &objects { cmd.arg(obj); } - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } match cmd.status() { @@ -72,7 +72,11 @@ pub fn build(ctx: &BuildContext) -> Result { } let name = ctx.output_filename.unwrap_or(ctx.project_name); let ext = ctx.output_extension.unwrap_or(""); - let final_name = if ext.is_empty() { name.to_string() } else { format!("{}.{}", name, ext) }; + let final_name = if ext.is_empty() { + name.to_string() + } else { + format!("{}.{}", name, ext) + }; let out_path = if ctx.kind == "sharedlib" { platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir) @@ -81,7 +85,7 @@ pub fn build(ctx: &BuildContext) -> Result { }; cmd.arg("-o").arg(out_path); - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } match cmd.status() { @@ -151,7 +155,7 @@ fn build_object( cmd.arg(flag); } - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } diff --git a/src/core/builder/unix_cc.rs b/src/core/builder/unix_cc.rs index ddd022c..25c04d7 100644 --- a/src/core/builder/unix_cc.rs +++ b/src/core/builder/unix_cc.rs @@ -30,7 +30,7 @@ pub fn build(ctx: &BuildContext) -> Result { for obj in &objects { cmd.arg(obj); } - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } match cmd.status() { @@ -65,7 +65,11 @@ pub fn build(ctx: &BuildContext) -> Result { } let name = ctx.output_filename.unwrap_or(ctx.project_name); let ext = ctx.output_extension.unwrap_or(""); - let final_name = if ext.is_empty() { name.to_string() } else { format!("{}.{}", name, ext) }; + let final_name = if ext.is_empty() { + name.to_string() + } else { + format!("{}.{}", name, ext) + }; let out_path = if ctx.kind == "sharedlib" { platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir) @@ -74,7 +78,7 @@ pub fn build(ctx: &BuildContext) -> Result { }; cmd.arg("-o").arg(out_path); - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } match cmd.status() { @@ -214,7 +218,7 @@ fn build_object( let d_path = Path::new(obj_path).with_extension("d"); cmd.arg("-MMD").arg("-MF").arg(&d_path); - if std::env::var("DCR_DEBUG").is_ok() { + if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() { eprintln!("[dcr] {:?}", cmd); } diff --git a/src/core/deps/register.rs b/src/core/deps/register.rs index f9d01f2..a3d1d4a 100644 --- a/src/core/deps/register.rs +++ b/src/core/deps/register.rs @@ -15,9 +15,23 @@ pub struct DcrConfig { pub registry: std::collections::HashMap, } +fn home_dir() -> Option { + if let Ok(home) = std::env::var("HOME") { + return Some(PathBuf::from(home)); + } + if let Ok(profile) = std::env::var("USERPROFILE") { + return Some(PathBuf::from(profile)); + } + None +} + +fn dcr_config_dir() -> Option { + home_dir().map(|h| h.join(".dcr")) +} + pub fn get_registry_config() -> Option { - let home = std::env::var("HOME").ok()?; - let config_path = Path::new(&home).join(".dcr/config.toml"); + let home = home_dir()?; + let config_path = home.join(".dcr/config.toml"); if !config_path.exists() { return None; } @@ -30,8 +44,9 @@ pub fn get_index_path() -> PathBuf { std::env::var("DCR_INDEX_PATH") .map(PathBuf::from) .unwrap_or_else(|_| { - let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string()); - Path::new(&home).join(".dcr").join("index.json") + dcr_config_dir() + .unwrap_or_else(|| PathBuf::from(".")) + .join("index.json") }) } diff --git a/src/core/registry.rs b/src/core/registry.rs index 3625855..ed36c15 100644 --- a/src/core/registry.rs +++ b/src/core/registry.rs @@ -20,10 +20,21 @@ pub struct RegistryManager { pub path: PathBuf, } +fn home_dir() -> Option { + if let Ok(home) = std::env::var("HOME") { + return Some(PathBuf::from(home)); + } + if let Ok(profile) = std::env::var("USERPROFILE") { + return Some(PathBuf::from(profile)); + } + None +} + impl RegistryManager { pub fn load() -> Result> { - let home = std::env::var("HOME")?; - let dcr_dir = PathBuf::from(home).join(".dcr"); + let dcr_dir = home_dir() + .ok_or("Cannot determine home directory")? + .join(".dcr"); let config_path = dcr_dir.join("config.toml"); if !config_path.exists() {