🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

practicode

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

practicode - npm Package Compare versions

Comparing version
0.1.18
to
0.1.19
+1
-1
Cargo.lock

@@ -456,3 +456,3 @@ # This file is automatically @generated by Cargo.

name = "practicode"
version = "0.1.18"
version = "0.1.19"
dependencies = [

@@ -459,0 +459,0 @@ "anyhow",

[package]
name = "practicode"
version = "0.1.18"
version = "0.1.19"
edition = "2024"

@@ -5,0 +5,0 @@ description = "Local-first coding-test practice in a Rust terminal UI with optional AI help."

{
"name": "practicode",
"version": "0.1.18",
"version": "0.1.19",
"description": "Local-first coding-test practice in a Rust terminal UI with optional AI help.",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -104,2 +104,4 @@ use super::*;

}
let mut ids = Vec::new();
let mut slugs = Vec::new();
for problem in bank {

@@ -117,2 +119,10 @@ if !is_safe_name(&problem.id) {

}
if ids.contains(&problem.id.as_str()) {
bail!("{} has duplicate problem id {}", path.display(), problem.id);
}
if slugs.contains(&problem.slug.as_str()) {
bail!("{} has duplicate slug {}", path.display(), problem.slug);
}
ids.push(problem.id.as_str());
slugs.push(problem.slug.as_str());
if problem.cases.is_empty() {

@@ -119,0 +129,0 @@ bail!(

@@ -70,3 +70,3 @@ use super::*;

pub fn normalize_next_source(source: &str) -> String {
if source == "ai" {
if source.trim().eq_ignore_ascii_case("ai") {
"ai".to_string()

@@ -79,3 +79,3 @@ } else {

pub fn normalize_ai_provider(provider: &str) -> String {
if provider == "claude" {
if provider.trim().eq_ignore_ascii_case("claude") {
"claude".to_string()

@@ -82,0 +82,0 @@ } else {

@@ -30,2 +30,4 @@ use super::*;

normalize_settings(&mut state.settings);
state.suggested_next_difficulty =
normalize_suggested_difficulty(&state.suggested_next_difficulty);
state.syntax_progress = normalize_syntax_progress(&state.syntax_progress);

@@ -78,2 +80,3 @@ state.current_syntax_lesson = normalize_current_syntax_lessons(&state.current_syntax_lesson);

settings.ui_language = normalize_ui_language(&settings.ui_language);
settings.theme = settings.theme.trim().to_lowercase();
if !THEMES.contains(&settings.theme.as_str()) {

@@ -103,1 +106,9 @@ settings.theme = "dark".to_string();

}
fn normalize_suggested_difficulty(difficulty: &str) -> String {
match normalize_difficulty(difficulty).as_str() {
"medium" => "medium".to_string(),
"hard" => "hard".to_string(),
_ => "easy".to_string(),
}
}
use anyhow::{Context, Result};
use std::{
env,
ffi::OsStr,
io::{ErrorKind, Write},
path::PathBuf,
path::{Path, PathBuf},
process::{Command, Stdio},

@@ -58,8 +59,29 @@ time::{Duration, SystemTime, UNIX_EPOCH},

let paths = env::var_os("PATH")?;
env::split_paths(&paths).find_map(|dir| {
let path = dir.join(name);
if path.is_file() { Some(path) } else { None }
})
let pathext = env::var_os("PATHEXT");
env::split_paths(&paths).find_map(|dir| find_in_dir(&dir, name, pathext.as_deref()))
}
fn find_in_dir(dir: &Path, name: &str, pathext: Option<&OsStr>) -> Option<PathBuf> {
let path = dir.join(name);
if path.is_file() {
return Some(path);
}
if Path::new(name).extension().is_some() {
return None;
}
pathext?
.to_string_lossy()
.split(';')
.filter(|ext| !ext.is_empty())
.map(|ext| {
let ext = if ext.starts_with('.') {
ext.to_string()
} else {
format!(".{ext}")
};
dir.join(format!("{name}{ext}"))
})
.find(|path| path.is_file())
}
pub fn shell_process(command: &str) -> Command {

@@ -106,2 +128,17 @@ if cfg!(windows) {

}
#[test]
fn which_honors_pathext_suffixes() {
let root = unique_temp_path("practicode-which", "dir");
std::fs::create_dir_all(&root).unwrap();
let exe = root.join("tool.CMD");
std::fs::write(&exe, "").unwrap();
assert_eq!(
find_in_dir(&root, "tool", Some(OsStr::new(".EXE;.CMD"))),
Some(exe)
);
let _ = std::fs::remove_dir_all(root);
}
}