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

View File

@@ -164,4 +164,75 @@ impl App {
self.commit_input();
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 jump_top: String,
pub jump_bottom: String,
pub append_item: String,
pub prepend_item: String,
}
impl Default for KeybindsConfig {
@@ -130,6 +132,8 @@ impl Default for KeybindsConfig {
previous_match: "N".to_string(),
jump_top: "gg".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::io::{self, Write};
use std::path::Path;
@@ -27,6 +27,7 @@ impl FormatHandler for EnvHandler {
depth: 0,
is_group: false,
status: ItemStatus::Present,
value_type: ValueType::String,
});
}
}
@@ -65,6 +66,7 @@ impl FormatHandler for EnvHandler {
depth: 0,
is_group: false,
status: ItemStatus::MissingFromTemplate,
value_type: ValueType::String,
});
}
}
@@ -159,6 +161,7 @@ mod tests {
depth: 0,
is_group: false,
status: ItemStatus::Present,
value_type: ValueType::String,
}];
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 std::fs;
use std::io;
@@ -58,6 +58,8 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
key_name.to_string()
} else if key_name.is_empty() {
prefix.to_string()
} else if key_name.starts_with('[') {
format!("{}{}", prefix, key_name)
} else {
format!("{}.{}", prefix, key_name)
};
@@ -74,6 +76,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth,
is_group: true,
status: ItemStatus::Present,
value_type: ValueType::Null,
});
}
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,
is_group: true,
status: ItemStatus::Present,
value_type: ValueType::Null,
});
}
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,
is_group: false,
status: ItemStatus::Present,
value_type: ValueType::String,
});
}
Value::Number(n) => {
@@ -123,6 +128,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth,
is_group: false,
status: ItemStatus::Present,
value_type: ValueType::Number,
});
}
Value::Bool(b) => {
@@ -136,6 +142,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth,
is_group: false,
status: ItemStatus::Present,
value_type: ValueType::Bool,
});
}
Value::Null => {
@@ -148,6 +155,7 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
depth,
is_group: false,
status: ItemStatus::Present,
value_type: ValueType::Null,
});
}
}
@@ -205,14 +213,14 @@ impl FormatHandler for HierarchicalHandler {
let val = var.value.as_deref()
.or(var.template_value.as_deref())
.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)
}
}
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 last_part = match parts.next_back() {
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();
// Attempt basic type inference
let final_val = if let Ok(n) = new_val_str.parse::<i64>() {
Value::Number(n.into())
} else if let Ok(b) = new_val_str.parse::<bool>() {
Value::Bool(b)
} else {
Value::String(new_val_str.to_string())
// Use the preserved ValueType instead of aggressive inference
let final_val = match value_type {
ValueType::Number => {
if let Ok(n) = new_val_str.parse::<i64>() {
Value::Number(n.into())
} else if let Ok(f) = new_val_str.parse::<f64>() {
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 {
@@ -316,7 +341,7 @@ mod tests {
let mut root = Value::Object(Map::new());
for var in vars {
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,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueType {
String,
Number,
Bool,
Null,
}
#[derive(Debug, Clone)]
pub struct ConfigItem {
pub key: String,
@@ -22,6 +30,7 @@ pub struct ConfigItem {
pub depth: usize,
pub is_group: bool,
pub status: ItemStatus,
pub value_type: ValueType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@@ -122,6 +122,8 @@ where
(&self.config.keybinds.previous_match, "previous_match"),
(&self.config.keybinds.jump_top, "jump_top"),
(&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"),
(&":".to_string(), "command"),
(&"q".to_string(), "quit"),
@@ -154,6 +156,8 @@ where
"previous_match" => self.app.jump_previous_match(),
"jump_top" => self.app.jump_top(),
"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(),
"command" => {
self.command_buffer.push(':');