fixed string to int error

This commit is contained in:
2026-03-17 13:09:22 +01:00
parent f09dbc8321
commit 94ff632b39
10 changed files with 141 additions and 21 deletions

2
Cargo.lock generated
View File

@@ -853,7 +853,7 @@ dependencies = [
[[package]] [[package]]
name = "mould" name = "mould"
version = "0.4.0" version = "0.4.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",

View File

@@ -1,9 +1,3 @@
[package]
edition = "2024"
name = "mould"
version = "0.4.0"
authors = ["Nils Pukropp <nils@narl.io>"]
[[bin]] [[bin]]
name = "mould" name = "mould"
path = "src/main.rs" path = "src/main.rs"
@@ -22,12 +16,18 @@ toml = "1.0.6"
tui-input = "0.15.0" tui-input = "0.15.0"
[dependencies.clap] [dependencies.clap]
version = "4.6.0"
features = ["derive"] features = ["derive"]
version = "4.6.0"
[dependencies.serde] [dependencies.serde]
version = "1.0.228"
features = ["derive"] features = ["derive"]
version = "1.0.228"
[dev-dependencies] [dev-dependencies]
tempfile = "3.27.0" tempfile = "3.27.0"
[package]
authors = ["Nils Pukropp <nils@narl.io>"]
edition = "2024"
name = "mould"
version = "0.4.1"

View File

@@ -61,6 +61,8 @@ mould config.template.json -o config.json
- `gg`: Jump to the top - `gg`: Jump to the top
- `G`: Jump to the bottom - `G`: Jump to the bottom
- `i`: Edit the value of the currently selected key (Enter Insert Mode) - `i`: Edit the value of the currently selected key (Enter Insert Mode)
- `o`: Append a new item to the current array
- `O`: Prepend a new item to the current array
- `a`: Add missing value from template to active config - `a`: Add missing value from template to active config
- `/`: Search for configuration keys (Jump to matches) - `/`: Search for configuration keys (Jump to matches)
- `n`: Jump to the next search match - `n`: Jump to the next search match

View File

@@ -39,3 +39,5 @@ next_match = "n"
previous_match = "N" previous_match = "N"
jump_top = "gg" jump_top = "gg"
jump_bottom = "G" jump_bottom = "G"
append_item = "o"
prepend_item = "O"

View File

@@ -164,4 +164,75 @@ impl App {
self.commit_input(); self.commit_input();
self.mode = Mode::Normal; self.mode = Mode::Normal;
} }
/// Adds a new item to an array if the selected item is part of one.
pub fn add_array_item(&mut self, after: bool) {
if self.vars.is_empty() {
return;
}
let (base, idx, depth) = {
let selected_item = &self.vars[self.selected];
if selected_item.is_group {
return;
}
let path = &selected_item.path;
if let Some((base, idx)) = parse_index(path) {
(base.to_string(), idx, selected_item.depth)
} else {
return;
}
};
let new_idx = if after { idx + 1 } else { idx };
let insert_pos = if after {
self.selected + 1
} else {
self.selected
};
// 1. Shift all items in this array that have index >= new_idx
for var in self.vars.iter_mut() {
if var.path.starts_with(&base) {
if let Some((b, i)) = parse_index(&var.path) {
if b == base && i >= new_idx {
var.path = format!("{}[{}]", base, i + 1);
// Also update key if it was just the index
if var.key == format!("[{}]", i) {
var.key = format!("[{}]", i + 1);
}
}
}
}
}
// 2. Insert new item
let new_item = ConfigItem {
key: format!("[{}]", new_idx),
path: format!("{}[{}]", base, new_idx),
value: Some("".to_string()),
template_value: None,
default_value: None,
depth,
is_group: false,
status: crate::format::ItemStatus::Modified,
value_type: crate::format::ValueType::String,
};
self.vars.insert(insert_pos, new_item);
self.selected = insert_pos;
self.sync_input_with_selected();
self.mode = Mode::Insert;
self.status_message = None;
}
}
fn parse_index(path: &str) -> Option<(&str, usize)> {
if path.ends_with(']') {
if let Some(start) = path.rfind('[') {
if let Ok(idx) = path[start + 1..path.len() - 1].parse::<usize>() {
return Some((&path[..start], idx));
}
}
}
None
} }

View File

@@ -114,6 +114,8 @@ pub struct KeybindsConfig {
pub previous_match: String, pub previous_match: String,
pub jump_top: String, pub jump_top: String,
pub jump_bottom: String, pub jump_bottom: String,
pub append_item: String,
pub prepend_item: String,
} }
impl Default for KeybindsConfig { impl Default for KeybindsConfig {
@@ -130,6 +132,8 @@ impl Default for KeybindsConfig {
previous_match: "N".to_string(), previous_match: "N".to_string(),
jump_top: "gg".to_string(), jump_top: "gg".to_string(),
jump_bottom: "G".to_string(), jump_bottom: "G".to_string(),
append_item: "o".to_string(),
prepend_item: "O".to_string(),
} }
} }
} }

View File

@@ -1,4 +1,4 @@
use super::{ConfigItem, FormatHandler, ItemStatus}; use super::{ConfigItem, FormatHandler, ItemStatus, ValueType};
use std::fs; use std::fs;
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::Path; use std::path::Path;
@@ -27,6 +27,7 @@ impl FormatHandler for EnvHandler {
depth: 0, depth: 0,
is_group: false, is_group: false,
status: ItemStatus::Present, status: ItemStatus::Present,
value_type: ValueType::String,
}); });
} }
} }
@@ -65,6 +66,7 @@ impl FormatHandler for EnvHandler {
depth: 0, depth: 0,
is_group: false, is_group: false,
status: ItemStatus::MissingFromTemplate, status: ItemStatus::MissingFromTemplate,
value_type: ValueType::String,
}); });
} }
} }
@@ -159,6 +161,7 @@ mod tests {
depth: 0, depth: 0,
is_group: false, is_group: false,
status: ItemStatus::Present, status: ItemStatus::Present,
value_type: ValueType::String,
}]; }];
let handler = EnvHandler; let handler = EnvHandler;

View File

@@ -1,4 +1,4 @@
use super::{ConfigItem, FormatHandler, FormatType, ItemStatus}; use super::{ConfigItem, FormatHandler, FormatType, ItemStatus, ValueType};
use serde_json::{Map, Value}; use serde_json::{Map, Value};
use std::fs; use std::fs;
use std::io; use std::io;
@@ -58,6 +58,8 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
key_name.to_string() key_name.to_string()
} else if key_name.is_empty() { } else if key_name.is_empty() {
prefix.to_string() prefix.to_string()
} else if key_name.starts_with('[') {
format!("{}{}", prefix, key_name)
} else { } else {
format!("{}.{}", prefix, key_name) format!("{}.{}", prefix, key_name)
}; };
@@ -74,6 +76,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth, depth,
is_group: true, is_group: true,
status: ItemStatus::Present, status: ItemStatus::Present,
value_type: ValueType::Null,
}); });
} }
let next_depth = if path.is_empty() { depth } else { depth + 1 }; let next_depth = if path.is_empty() { depth } else { depth + 1 };
@@ -92,6 +95,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth, depth,
is_group: true, is_group: true,
status: ItemStatus::Present, status: ItemStatus::Present,
value_type: ValueType::Null,
}); });
} }
let next_depth = if path.is_empty() { depth } else { depth + 1 }; let next_depth = if path.is_empty() { depth } else { depth + 1 };
@@ -110,6 +114,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth, depth,
is_group: false, is_group: false,
status: ItemStatus::Present, status: ItemStatus::Present,
value_type: ValueType::String,
}); });
} }
Value::Number(n) => { Value::Number(n) => {
@@ -123,6 +128,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth, depth,
is_group: false, is_group: false,
status: ItemStatus::Present, status: ItemStatus::Present,
value_type: ValueType::Number,
}); });
} }
Value::Bool(b) => { Value::Bool(b) => {
@@ -136,6 +142,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth, depth,
is_group: false, is_group: false,
status: ItemStatus::Present, status: ItemStatus::Present,
value_type: ValueType::Bool,
}); });
} }
Value::Null => { Value::Null => {
@@ -148,6 +155,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth, depth,
is_group: false, is_group: false,
status: ItemStatus::Present, status: ItemStatus::Present,
value_type: ValueType::Null,
}); });
} }
} }
@@ -205,14 +213,14 @@ impl FormatHandler for HierarchicalHandler {
let val = var.value.as_deref() let val = var.value.as_deref()
.or(var.template_value.as_deref()) .or(var.template_value.as_deref())
.unwrap_or(""); .unwrap_or("");
insert_into_value(&mut root, &var.path, val); insert_into_value(&mut root, &var.path, val, var.value_type);
} }
} }
self.write_value(path, &root) self.write_value(path, &root)
} }
} }
fn insert_into_value(root: &mut Value, path: &str, new_val_str: &str) { fn insert_into_value(root: &mut Value, path: &str, new_val_str: &str, value_type: ValueType) {
let mut parts = path.split('.'); let mut parts = path.split('.');
let last_part = match parts.next_back() { let last_part = match parts.next_back() {
Some(p) => p, Some(p) => p,
@@ -255,13 +263,30 @@ fn insert_into_value(root: &mut Value, path: &str, new_val_str: &str) {
} }
let map = current.as_object_mut().unwrap(); let map = current.as_object_mut().unwrap();
// Attempt basic type inference // Use the preserved ValueType instead of aggressive inference
let final_val = if let Ok(n) = new_val_str.parse::<i64>() { let final_val = match value_type {
Value::Number(n.into()) ValueType::Number => {
} else if let Ok(b) = new_val_str.parse::<bool>() { if let Ok(n) = new_val_str.parse::<i64>() {
Value::Bool(b) Value::Number(n.into())
} else { } else if let Ok(f) = new_val_str.parse::<f64>() {
Value::String(new_val_str.to_string()) if let Some(n) = serde_json::Number::from_f64(f) {
Value::Number(n)
} else {
Value::String(new_val_str.to_string())
}
} else {
Value::String(new_val_str.to_string())
}
}
ValueType::Bool => {
if let Ok(b) = new_val_str.parse::<bool>() {
Value::Bool(b)
} else {
Value::String(new_val_str.to_string())
}
}
ValueType::Null if new_val_str.is_empty() => Value::Null,
_ => Value::String(new_val_str.to_string()),
}; };
if let Some(i) = final_idx { if let Some(i) = final_idx {
@@ -316,7 +341,7 @@ mod tests {
let mut root = Value::Object(Map::new()); let mut root = Value::Object(Map::new());
for var in vars { for var in vars {
if !var.is_group { if !var.is_group {
insert_into_value(&mut root, &var.path, var.value.as_deref().unwrap_or("")); insert_into_value(&mut root, &var.path, var.value.as_deref().unwrap_or(""), var.value_type);
} }
} }

View File

@@ -12,6 +12,14 @@ pub enum ItemStatus {
Modified, Modified,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueType {
String,
Number,
Bool,
Null,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ConfigItem { pub struct ConfigItem {
pub key: String, pub key: String,
@@ -22,6 +30,7 @@ pub struct ConfigItem {
pub depth: usize, pub depth: usize,
pub is_group: bool, pub is_group: bool,
pub status: ItemStatus, pub status: ItemStatus,
pub value_type: ValueType,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@@ -122,6 +122,8 @@ where
(&self.config.keybinds.previous_match, "previous_match"), (&self.config.keybinds.previous_match, "previous_match"),
(&self.config.keybinds.jump_top, "jump_top"), (&self.config.keybinds.jump_top, "jump_top"),
(&self.config.keybinds.jump_bottom, "jump_bottom"), (&self.config.keybinds.jump_bottom, "jump_bottom"),
(&self.config.keybinds.append_item, "append_item"),
(&self.config.keybinds.prepend_item, "prepend_item"),
(&"a".to_string(), "add_missing"), (&"a".to_string(), "add_missing"),
(&":".to_string(), "command"), (&":".to_string(), "command"),
(&"q".to_string(), "quit"), (&"q".to_string(), "quit"),
@@ -154,6 +156,8 @@ where
"previous_match" => self.app.jump_previous_match(), "previous_match" => self.app.jump_previous_match(),
"jump_top" => self.app.jump_top(), "jump_top" => self.app.jump_top(),
"jump_bottom" => self.app.jump_bottom(), "jump_bottom" => self.app.jump_bottom(),
"append_item" => self.app.add_array_item(true),
"prepend_item" => self.app.add_array_item(false),
"add_missing" => self.add_missing_item(), "add_missing" => self.add_missing_item(),
"command" => { "command" => {
self.command_buffer.push(':'); self.command_buffer.push(':');