organize
This commit is contained in:
@@ -7,7 +7,7 @@ use ratatui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
clipboard::Clipboard, error_msg::StatusMessage, logic::calc::{CellType, Grid}, mode::Mode, screen::ScreenSpace
|
clipboard::Clipboard, error_msg::StatusMessage, logic::{calc::Grid, cell::CellType}, mode::Mode, screen::ScreenSpace
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
use std::cmp::min;
|
|
||||||
|
|
||||||
use evalexpr::eval_with_context;
|
use evalexpr::eval_with_context;
|
||||||
|
|
||||||
use crate::app::logic::{
|
use crate::app::logic::{calc::Grid, cell::CellType, ctx::ExtractionContext};
|
||||||
calc::{CellType, Grid},
|
|
||||||
ctx::ExtractionContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
@@ -62,7 +57,7 @@ impl Clipboard {
|
|||||||
|
|
||||||
if translate {
|
if translate {
|
||||||
if let Some(cell) = cell {
|
if let Some(cell) = cell {
|
||||||
let trans = Clipboard::translate_cell(cell, self.source_cell, into.cursor());
|
let trans = cell.translate_cell(self.source_cell, into.cursor());
|
||||||
into.set_cell_raw(idx, Some(trans));
|
into.set_cell_raw(idx, Some(trans));
|
||||||
} else {
|
} else {
|
||||||
// cell doesn't exist, no need to translate
|
// cell doesn't exist, no need to translate
|
||||||
@@ -80,44 +75,6 @@ impl Clipboard {
|
|||||||
self.last_paste_cell = (cx, cy);
|
self.last_paste_cell = (cx, cy);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_cell(cell: &CellType, from: (usize, usize), to: (usize, usize)) -> CellType {
|
|
||||||
match cell {
|
|
||||||
// don't translate non-equations
|
|
||||||
CellType::Number(_) | CellType::String(_) => return cell.clone(),
|
|
||||||
CellType::Equation(eq) => {
|
|
||||||
// extract all the variables
|
|
||||||
let ctx = ExtractionContext::new();
|
|
||||||
let _ = eval_with_context(eq, &ctx);
|
|
||||||
|
|
||||||
let mut rolling = eq.clone();
|
|
||||||
// translate standard vars A0 -> A1
|
|
||||||
for old_var in ctx.dump_vars() {
|
|
||||||
if let Some((src_x, src_y)) = Grid::parse_to_idx(&old_var) {
|
|
||||||
let (x1, y1) = from;
|
|
||||||
let x1 = x1 as i32;
|
|
||||||
let y1 = y1 as i32;
|
|
||||||
let (x2, y2) = to;
|
|
||||||
let x2 = x2 as i32;
|
|
||||||
let y2 = y2 as i32;
|
|
||||||
|
|
||||||
let dest_x = (src_x as i32 + (x2 - x1)) as usize;
|
|
||||||
let dest_y = (src_y as i32 + (y2 - y1)) as usize;
|
|
||||||
|
|
||||||
let alpha = Grid::num_to_char(dest_x);
|
|
||||||
let alpha = alpha.trim();
|
|
||||||
let new_var = format!("{alpha}{dest_y}");
|
|
||||||
|
|
||||||
// swap out vars
|
|
||||||
rolling = rolling.replace(&old_var, &new_var);
|
|
||||||
} else {
|
|
||||||
// why you coping invalid stuff, nerd?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rolling.into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clones data from Grid into self.
|
/// Clones data from Grid into self.
|
||||||
/// Start and end don't have to be sorted in any sort of way. The function works with
|
/// Start and end don't have to be sorted in any sort of way. The function works with
|
||||||
/// any two points.
|
/// any two points.
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ use std::{
|
|||||||
|
|
||||||
use evalexpr::*;
|
use evalexpr::*;
|
||||||
|
|
||||||
use crate::app::logic::ctx;
|
use crate::app::logic::{
|
||||||
|
cell::{CSV_DELIMITER, CellType},
|
||||||
|
ctx,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::app::app::App;
|
use crate::app::app::App;
|
||||||
@@ -34,135 +37,22 @@ impl std::fmt::Debug for Grid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CSV_DELIMITER: char = ',';
|
|
||||||
const CSV_ESCAPE: char = '"';
|
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
pub fn cursor(&self) -> (usize, usize) {
|
pub fn new() -> Self {
|
||||||
self.selected_cell
|
let mut a = Vec::with_capacity(LEN);
|
||||||
}
|
for _ in 0..LEN {
|
||||||
|
let mut b = Vec::with_capacity(LEN);
|
||||||
pub fn apply_momentum(&mut self, (x, y): (i32, i32)) {
|
for _ in 0..LEN {
|
||||||
let (cx, cy) = self.cursor();
|
b.push(None)
|
||||||
|
|
||||||
assert_eq!(0i32.saturating_add(-1), -1);
|
|
||||||
let x = (cx as i32).saturating_add(x);
|
|
||||||
let y = (cy as i32).saturating_add(y);
|
|
||||||
|
|
||||||
// make it positive
|
|
||||||
let x = max(x, 0) as usize;
|
|
||||||
let y = max(y, 0) as usize;
|
|
||||||
|
|
||||||
// keep it in the grid
|
|
||||||
let x = min(x, LEN - 1);
|
|
||||||
let y = min(y, LEN - 1);
|
|
||||||
|
|
||||||
self.mv_cursor_to(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mv_cursor_to(&mut self, x: usize, y: usize) {
|
|
||||||
self.selected_cell = (x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn needs_to_be_saved(&self) -> bool {
|
|
||||||
self.dirty
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save file to `path` as a csv. Path with have `csv` appended to it if it
|
|
||||||
/// does not already have the extension.
|
|
||||||
pub fn save_to(&mut self, path: impl Into<PathBuf>) -> std::io::Result<()> {
|
|
||||||
let mut path = path.into();
|
|
||||||
|
|
||||||
const CSV: &str = "csv";
|
|
||||||
const CUSTOM_EXT: &str = "nscim";
|
|
||||||
|
|
||||||
let resolve_values;
|
|
||||||
|
|
||||||
match path.extension() {
|
|
||||||
Some(ext) => {
|
|
||||||
match ext.to_str() {
|
|
||||||
Some(CSV) => {
|
|
||||||
resolve_values = true;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
resolve_values = false;
|
|
||||||
path.add_extension(CUSTOM_EXT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
resolve_values = false;
|
|
||||||
path.add_extension(CUSTOM_EXT);
|
|
||||||
}
|
}
|
||||||
|
a.push(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut f = fs::OpenOptions::new().write(true).append(false).truncate(true).create(true).open(path)?;
|
Self {
|
||||||
let (mx, my) = self.max();
|
cells: a,
|
||||||
for y in 0..=my {
|
selected_cell: (0, 0),
|
||||||
for x in 0..=mx {
|
dirty: false,
|
||||||
let cell = &self.cells[x][y];
|
|
||||||
|
|
||||||
let data = if let Some(cell) = cell {
|
|
||||||
if let Ok(val) = self.evaluate(&cell.to_string()) && resolve_values {
|
|
||||||
val.to_string()
|
|
||||||
} else {
|
|
||||||
cell.to_string_csv_escaped()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CSV_DELIMITER.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "{data}")?;
|
|
||||||
}
|
|
||||||
write!(f, "\n")?;
|
|
||||||
}
|
}
|
||||||
f.flush()?;
|
|
||||||
|
|
||||||
self.dirty = false;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn max_y_at_x(&self, x: usize) -> usize {
|
|
||||||
let mut max_y = 0;
|
|
||||||
if let Some(col) = self.cells.get(x) {
|
|
||||||
// we could do fancy things like .take_while(not null) but then
|
|
||||||
// we would have to deal with empty cells and stuff, which sounds
|
|
||||||
// boring. This will be fast "enough", considering the grid is
|
|
||||||
// probably only like 1k cells
|
|
||||||
for (yi, cell) in col.iter().enumerate() {
|
|
||||||
if cell.is_some() {
|
|
||||||
if yi > max_y {
|
|
||||||
max_y = yi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
max_y
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over the entire grid and see where
|
|
||||||
/// the farthest modified cell is.
|
|
||||||
#[must_use]
|
|
||||||
fn max(&self) -> (usize, usize) {
|
|
||||||
let mut max_x = 0;
|
|
||||||
let mut max_y = 0;
|
|
||||||
|
|
||||||
for (xi, x) in self.cells.iter().enumerate() {
|
|
||||||
for (yi, cell) in x.iter().enumerate() {
|
|
||||||
if cell.is_some() {
|
|
||||||
if yi > max_y {
|
|
||||||
max_y = yi
|
|
||||||
}
|
|
||||||
if xi > max_x {
|
|
||||||
max_x = xi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(max_x, max_y)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_file(path: impl Into<PathBuf>) -> std::io::Result<Self> {
|
pub fn new_from_file(path: impl Into<PathBuf>) -> std::io::Result<Self> {
|
||||||
@@ -186,6 +76,64 @@ impl Grid {
|
|||||||
Ok(grid)
|
Ok(grid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Save file to `path` as a csv. Path with have `csv` appended to it if it
|
||||||
|
/// does not already have the extension.
|
||||||
|
pub fn save_to(&mut self, path: impl Into<PathBuf>) -> std::io::Result<()> {
|
||||||
|
let mut path = path.into();
|
||||||
|
|
||||||
|
const CSV: &str = "csv";
|
||||||
|
const CUSTOM_EXT: &str = "nscim";
|
||||||
|
|
||||||
|
let resolve_values;
|
||||||
|
|
||||||
|
match path.extension() {
|
||||||
|
Some(ext) => match ext.to_str() {
|
||||||
|
Some(CSV) => {
|
||||||
|
resolve_values = true;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
resolve_values = false;
|
||||||
|
path.add_extension(CUSTOM_EXT);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
resolve_values = false;
|
||||||
|
path.add_extension(CUSTOM_EXT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut f = fs::OpenOptions::new().write(true).append(false).truncate(true).create(true).open(path)?;
|
||||||
|
let (mx, my) = self.max();
|
||||||
|
for y in 0..=my {
|
||||||
|
for x in 0..=mx {
|
||||||
|
let cell = &self.cells[x][y];
|
||||||
|
|
||||||
|
let data = if let Some(cell) = cell {
|
||||||
|
if let Ok(val) = self.evaluate(&cell.to_string())
|
||||||
|
&& resolve_values
|
||||||
|
{
|
||||||
|
val.to_string()
|
||||||
|
} else {
|
||||||
|
cell.to_string_csv_escaped()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CSV_DELIMITER.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{data}")?;
|
||||||
|
}
|
||||||
|
write!(f, "\n")?;
|
||||||
|
}
|
||||||
|
f.flush()?;
|
||||||
|
|
||||||
|
self.dirty = false;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn needs_to_be_saved(&self) -> bool {
|
||||||
|
self.dirty
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_csv_line(line: &str) -> Vec<Option<String>> {
|
fn parse_csv_line(line: &str) -> Vec<Option<String>> {
|
||||||
let mut iter = line.as_bytes().iter().map(|f| *f as char).peekable();
|
let mut iter = line.as_bytes().iter().map(|f| *f as char).peekable();
|
||||||
let mut cells = Vec::new();
|
let mut cells = Vec::new();
|
||||||
@@ -261,21 +209,73 @@ impl Grid {
|
|||||||
cells
|
cells
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn cursor(&self) -> (usize, usize) {
|
||||||
let mut a = Vec::with_capacity(LEN);
|
self.selected_cell
|
||||||
for _ in 0..LEN {
|
}
|
||||||
let mut b = Vec::with_capacity(LEN);
|
|
||||||
for _ in 0..LEN {
|
pub fn mv_cursor_to(&mut self, x: usize, y: usize) {
|
||||||
b.push(None)
|
self.selected_cell = (x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_momentum(&mut self, (x, y): (i32, i32)) {
|
||||||
|
let (cx, cy) = self.cursor();
|
||||||
|
|
||||||
|
assert_eq!(0i32.saturating_add(-1), -1);
|
||||||
|
let x = (cx as i32).saturating_add(x);
|
||||||
|
let y = (cy as i32).saturating_add(y);
|
||||||
|
|
||||||
|
// make it positive
|
||||||
|
let x = max(x, 0) as usize;
|
||||||
|
let y = max(y, 0) as usize;
|
||||||
|
|
||||||
|
// keep it in the grid
|
||||||
|
let x = min(x, LEN - 1);
|
||||||
|
let y = min(y, LEN - 1);
|
||||||
|
|
||||||
|
self.mv_cursor_to(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over the entire grid and see where
|
||||||
|
/// the farthest modified cell is.
|
||||||
|
#[must_use]
|
||||||
|
fn max(&self) -> (usize, usize) {
|
||||||
|
let mut max_x = 0;
|
||||||
|
let mut max_y = 0;
|
||||||
|
|
||||||
|
for (xi, x) in self.cells.iter().enumerate() {
|
||||||
|
for (yi, cell) in x.iter().enumerate() {
|
||||||
|
if cell.is_some() {
|
||||||
|
if yi > max_y {
|
||||||
|
max_y = yi
|
||||||
|
}
|
||||||
|
if xi > max_x {
|
||||||
|
max_x = xi
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
a.push(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
(max_x, max_y)
|
||||||
cells: a,
|
}
|
||||||
selected_cell: (0, 0),
|
|
||||||
dirty: false,
|
#[must_use]
|
||||||
|
pub fn max_y_at_x(&self, x: usize) -> usize {
|
||||||
|
let mut max_y = 0;
|
||||||
|
if let Some(col) = self.cells.get(x) {
|
||||||
|
// we could do fancy things like .take_while(not null) but then
|
||||||
|
// we would have to deal with empty cells and stuff, which sounds
|
||||||
|
// boring. This will be fast "enough", considering the grid is
|
||||||
|
// probably only like 1k cells
|
||||||
|
for (yi, cell) in col.iter().enumerate() {
|
||||||
|
if cell.is_some() {
|
||||||
|
if yi > max_y {
|
||||||
|
max_y = yi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
max_y
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only evaluates equations, such as `=10` or `=A1/C2`, not
|
/// Only evaluates equations, such as `=10` or `=A1/C2`, not
|
||||||
@@ -405,65 +405,6 @@ impl Default for Grid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum CellType {
|
|
||||||
Number(f64),
|
|
||||||
String(String),
|
|
||||||
Equation(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<CellType> for f64 {
|
|
||||||
fn into(self) -> CellType {
|
|
||||||
CellType::duck_type(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<CellType> for String {
|
|
||||||
fn into(self) -> CellType {
|
|
||||||
CellType::duck_type(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CellType {
|
|
||||||
fn to_string_csv_escaped(&self) -> String {
|
|
||||||
let mut display = self.to_string();
|
|
||||||
|
|
||||||
// escape quotes " -> ""
|
|
||||||
let needs_escaping = display.char_indices().filter(|f| f.1 == CSV_ESCAPE).map(|f| f.0).collect::<Vec<usize>>();
|
|
||||||
for idx in needs_escaping.iter().rev() {
|
|
||||||
display.insert(*idx, CSV_ESCAPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// escape string of it has a comma
|
|
||||||
if display.contains(CSV_DELIMITER) {
|
|
||||||
format!("\"{display}\"{CSV_DELIMITER}")
|
|
||||||
} else {
|
|
||||||
format!("{display}{CSV_DELIMITER}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duck_type<'a>(value: impl Into<String>) -> Self {
|
|
||||||
let value = value.into();
|
|
||||||
|
|
||||||
if let Ok(parse) = value.parse::<f64>() {
|
|
||||||
Self::Number(parse)
|
|
||||||
} else {
|
|
||||||
if value.starts_with('=') { Self::Equation(value) } else { Self::String(value) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for CellType {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let d = match self {
|
|
||||||
CellType::Number(n) => n.to_string(),
|
|
||||||
CellType::String(n) => n.to_owned(),
|
|
||||||
CellType::Equation(r) => r.to_owned(),
|
|
||||||
};
|
|
||||||
write!(f, "{d}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do cells hold strings?
|
// Do cells hold strings?
|
||||||
#[test]
|
#[test]
|
||||||
fn cell_strings() {
|
fn cell_strings() {
|
||||||
|
|||||||
104
src/app/logic/cell.rs
Normal file
104
src/app/logic/cell.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use evalexpr::eval_with_context;
|
||||||
|
|
||||||
|
use crate::app::logic::{calc::Grid, ctx::ExtractionContext};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum CellType {
|
||||||
|
Number(f64),
|
||||||
|
String(String),
|
||||||
|
Equation(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<CellType> for f64 {
|
||||||
|
fn into(self) -> CellType {
|
||||||
|
CellType::duck_type(self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<CellType> for String {
|
||||||
|
fn into(self) -> CellType {
|
||||||
|
CellType::duck_type(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CSV_DELIMITER: char = ',';
|
||||||
|
const CSV_ESCAPE: char = '"';
|
||||||
|
|
||||||
|
impl CellType {
|
||||||
|
pub fn to_string_csv_escaped(&self) -> String {
|
||||||
|
let mut display = self.to_string();
|
||||||
|
|
||||||
|
// escape quotes " -> ""
|
||||||
|
let needs_escaping = display.char_indices().filter(|f| f.1 == CSV_ESCAPE).map(|f| f.0).collect::<Vec<usize>>();
|
||||||
|
for idx in needs_escaping.iter().rev() {
|
||||||
|
display.insert(*idx, CSV_ESCAPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// escape string of it has a comma
|
||||||
|
if display.contains(CSV_DELIMITER) {
|
||||||
|
format!("\"{display}\"{CSV_DELIMITER}")
|
||||||
|
} else {
|
||||||
|
format!("{display}{CSV_DELIMITER}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn duck_type<'a>(value: impl Into<String>) -> Self {
|
||||||
|
let value = value.into();
|
||||||
|
|
||||||
|
if let Ok(parse) = value.parse::<f64>() {
|
||||||
|
Self::Number(parse)
|
||||||
|
} else {
|
||||||
|
if value.starts_with('=') { Self::Equation(value) } else { Self::String(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn translate_cell(&self, from: (usize, usize), to: (usize, usize)) -> CellType {
|
||||||
|
match self {
|
||||||
|
// don't translate non-equations
|
||||||
|
CellType::Number(_) | CellType::String(_) => return self.clone(),
|
||||||
|
CellType::Equation(eq) => {
|
||||||
|
// extract all the variables
|
||||||
|
let ctx = ExtractionContext::new();
|
||||||
|
let _ = eval_with_context(eq, &ctx);
|
||||||
|
|
||||||
|
let mut rolling = eq.clone();
|
||||||
|
// translate standard vars A0 -> A1
|
||||||
|
for old_var in ctx.dump_vars() {
|
||||||
|
if let Some((src_x, src_y)) = Grid::parse_to_idx(&old_var) {
|
||||||
|
let (x1, y1) = from;
|
||||||
|
let x1 = x1 as i32;
|
||||||
|
let y1 = y1 as i32;
|
||||||
|
let (x2, y2) = to;
|
||||||
|
let x2 = x2 as i32;
|
||||||
|
let y2 = y2 as i32;
|
||||||
|
|
||||||
|
let dest_x = (src_x as i32 + (x2 - x1)) as usize;
|
||||||
|
let dest_y = (src_y as i32 + (y2 - y1)) as usize;
|
||||||
|
|
||||||
|
let alpha = Grid::num_to_char(dest_x);
|
||||||
|
let alpha = alpha.trim();
|
||||||
|
let new_var = format!("{alpha}{dest_y}");
|
||||||
|
|
||||||
|
// swap out vars
|
||||||
|
rolling = rolling.replace(&old_var, &new_var);
|
||||||
|
} else {
|
||||||
|
// why you coping invalid stuff, nerd?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rolling.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CellType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let d = match self {
|
||||||
|
CellType::Number(n) => n.to_string(),
|
||||||
|
CellType::String(n) => n.to_owned(),
|
||||||
|
CellType::Equation(r) => r.to_owned(),
|
||||||
|
};
|
||||||
|
write!(f, "{d}")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::RwLock};
|
|||||||
|
|
||||||
use evalexpr::{error::EvalexprResultValue, *};
|
use evalexpr::{error::EvalexprResultValue, *};
|
||||||
|
|
||||||
use crate::app::logic::calc::{CellType, Grid};
|
use crate::app::logic::{calc::Grid, cell::CellType};
|
||||||
|
|
||||||
pub struct CallbackContext<'a> {
|
pub struct CallbackContext<'a> {
|
||||||
variables: &'a Grid,
|
variables: &'a Grid,
|
||||||
@@ -155,9 +155,9 @@ impl<'a> Context for CallbackContext<'a> {
|
|||||||
|
|
||||||
if let Some(v) = self.variables.get_cell(identifier) {
|
if let Some(v) = self.variables.get_cell(identifier) {
|
||||||
match v {
|
match v {
|
||||||
super::calc::CellType::Number(n) => return Some(Value::Float(n.to_owned())),
|
CellType::Number(n) => return Some(Value::Float(n.to_owned())),
|
||||||
super::calc::CellType::String(s) => return Some(Value::String(s.to_owned())),
|
CellType::String(s) => return Some(Value::String(s.to_owned())),
|
||||||
super::calc::CellType::Equation(eq) => {
|
CellType::Equation(eq) => {
|
||||||
if let Ok(mut depth) = self.eval_depth.write() {
|
if let Ok(mut depth) = self.eval_depth.write() {
|
||||||
*depth += 1;
|
*depth += 1;
|
||||||
if *depth > RECURSION_DEPTH_LIMIT {
|
if *depth > RECURSION_DEPTH_LIMIT {
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod calc;
|
pub mod calc;
|
||||||
pub mod ctx;
|
pub mod ctx;
|
||||||
|
pub mod cell;
|
||||||
Reference in New Issue
Block a user