Add macos/ios app using rust lib
This commit is contained in:
48
src/lib.rs
48
src/lib.rs
@@ -1,4 +1,8 @@
|
||||
mod move_money;
|
||||
use std::ffi::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
|
||||
pub use self::move_money::*;
|
||||
|
||||
mod smush_rules;
|
||||
@@ -6,3 +10,47 @@ pub use self::smush_rules::*;
|
||||
|
||||
mod overhead_allocation;
|
||||
pub use self::overhead_allocation::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn move_money_from_text(
|
||||
rules: *const c_char,
|
||||
lines: *const c_char,
|
||||
use_numeric_accounts: bool,
|
||||
) -> *mut c_char {
|
||||
let mut output_writer = csv::Writer::from_writer(vec![]);
|
||||
let safe_rules = unsafe {
|
||||
assert!(!rules.is_null());
|
||||
CStr::from_ptr(rules)
|
||||
};
|
||||
let safe_lines = unsafe {
|
||||
assert!(!lines.is_null());
|
||||
CStr::from_ptr(lines)
|
||||
};
|
||||
move_money(
|
||||
csv::Reader::from_reader(safe_rules.to_bytes()),
|
||||
csv::Reader::from_reader(safe_lines.to_bytes()),
|
||||
&mut output_writer,
|
||||
use_numeric_accounts,
|
||||
);
|
||||
// TODO: Replace all these unwraps with something more elegant
|
||||
let inner = output_writer.into_inner().unwrap();
|
||||
CString::new(String::from_utf8(inner).unwrap())
|
||||
.unwrap()
|
||||
.into_raw()
|
||||
|
||||
// Also some resources I looked at, in case things aren't going right:
|
||||
// https://notes.huy.rocks/en/string-ffi-rust.html
|
||||
// http://jakegoulding.com/rust-ffi-omnibus/string_return/
|
||||
// https://rust-unofficial.github.io/patterns/idioms/ffi/passing-strings.html
|
||||
// This looks like exactly what I'm doing too: https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.htmlcar
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn move_money_from_text_free(s: *mut c_char) {
|
||||
unsafe {
|
||||
if s.is_null() {
|
||||
return;
|
||||
}
|
||||
CString::from_raw(s)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashMap, error::Error, io::Write, path::PathBuf};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use coster_rs::{CsvCost, Unit};
|
||||
use coster_rs::CsvCost;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -79,7 +79,7 @@ fn move_money(
|
||||
coster_rs::move_money(
|
||||
csv::Reader::from_path(rules)?,
|
||||
csv::Reader::from_path(lines)?,
|
||||
csv::Writer::from_path(output.unwrap_or(PathBuf::from("output.csv")))?,
|
||||
&mut csv::Writer::from_path(output.unwrap_or(PathBuf::from("output.csv")))?,
|
||||
use_numeric_accounts,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ pub struct CsvCost {
|
||||
pub fn move_money<R, L, O>(
|
||||
rules_reader: csv::Reader<R>,
|
||||
lines_reader: csv::Reader<L>,
|
||||
output: csv::Writer<O>,
|
||||
output: &mut csv::Writer<O>,
|
||||
use_numeric_accounts: bool,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
@@ -326,7 +326,7 @@ mod tests {
|
||||
super::move_money(
|
||||
csv::Reader::from_path("reclassrule.csv").unwrap(),
|
||||
csv::Reader::from_path("line.csv").unwrap(),
|
||||
csv::Writer::from_path("output.csv").unwrap(),
|
||||
&mut csv::Writer::from_path("output.csv").unwrap(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, io::Read, path::Path};
|
||||
|
||||
use itertools::Itertools;
|
||||
use nalgebra::{DMatrix, Dynamic, LU};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum DepartmentType {
|
||||
@@ -9,6 +10,36 @@ pub enum DepartmentType {
|
||||
Overhead,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CsvAllocationStatistic {
|
||||
#[serde(rename = "Name")]
|
||||
name: String,
|
||||
#[serde(rename = "Description")]
|
||||
description: Option<String>,
|
||||
#[serde(rename = "AccountType")]
|
||||
account_type: String,
|
||||
#[serde(rename = "AccountRanges")]
|
||||
account_ranges: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CsvAccount {
|
||||
#[serde(rename = "Code")]
|
||||
code: String,
|
||||
#[serde(rename = "Description")]
|
||||
description: Option<String>,
|
||||
#[serde(rename = "Type")]
|
||||
account_type: String,
|
||||
#[serde(rename = "CostOutput")]
|
||||
cost_output: Option<String>,
|
||||
#[serde(rename = "PercentFixed")]
|
||||
percent_fixed: f64,
|
||||
}
|
||||
|
||||
type CsvCostCentre = HashMap<String, String>;
|
||||
|
||||
type CsvArea = HashMap<String, String>;
|
||||
|
||||
// Note: remember these are overhead departments only when calculating the lu decomposition or pseudoinverse, and for each department,
|
||||
// you either need -1 or rest negative for a row to subtract the initial amounts so we end up effectively 0 (simultaneous equations end
|
||||
// up with negative there so yes this is expected)
|
||||
@@ -65,10 +96,32 @@ fn get_rules_indexes(
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn reciprocal_allocation<Lines, Account, AllocationStatistic, Area, CostCentre, Output>(
|
||||
lines: csv::Reader<Lines>,
|
||||
accounts: csv::Reader<Account>,
|
||||
allocation_statistics: csv::Reader<AllocationStatistic>,
|
||||
areas: csv::Reader<Area>,
|
||||
cost_centres: csv::Reader<CostCentre>,
|
||||
output: csv::Writer<Output>,
|
||||
) where
|
||||
Lines: Read,
|
||||
Account: Read,
|
||||
AllocationStatistic: Read,
|
||||
Area: Read,
|
||||
CostCentre: Read,
|
||||
Output: std::io::Write,
|
||||
{
|
||||
// Build out the the list of allocation rules from areas/allocation statistics (similar to ppm building 'cost drivers')
|
||||
|
||||
// do reciprocal allocation (only for variable portion of accounts), for each account
|
||||
|
||||
// Copy across fixed stuff (if necessary, not sure it is)
|
||||
}
|
||||
|
||||
// Perform the reciprocal allocation (matrix) method to allocate servicing departments (indirect) costs
|
||||
// to functional departments. Basically just a matrix solve, uses regression (moore-penrose pseudoinverse) when
|
||||
// matrix is singular
|
||||
pub fn reciprocal_allocation(
|
||||
fn reciprocal_allocation_impl(
|
||||
allocations: Vec<OverheadAllocationRule>,
|
||||
account_costs: Vec<AccountCost>,
|
||||
// TODO: Throw an appropriate error
|
||||
|
||||
Reference in New Issue
Block a user