Skip to content

Commit

Permalink
Merge pull request #247 from zhang-accounting/feat/wasm-return-full-s…
Browse files Browse the repository at this point in the history
…tore

Feat/wasm return full store
  • Loading branch information
Kilerd committed Feb 28, 2024
2 parents d614313 + 3038ed5 commit bb0e298
Show file tree
Hide file tree
Showing 10 changed files with 775 additions and 998 deletions.
1,548 changes: 605 additions & 943 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions bindings/wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "zhang-wasm"
version = "0.1.0"
version = "0.1.1"
authors = ["Kilerd <blove694@gmail.com>"]
edition = "2018"

Expand All @@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"]
default = ["console_error_panic_hook"]

[dependencies]
wasm-bindgen = "0.2.63"
wasm-bindgen = { version = "0.2.91", features = ["serde-serialize"] }

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
Expand All @@ -25,8 +25,12 @@ console_error_panic_hook = { version = "0.1.6", optional = true }
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true }
zhang-core = { path="../../zhang-core", features=["wasm"]}
zhang-core = { path = "../../zhang-core", features = ["wasm"] }
zhang-ast = { path = "../../zhang-ast" }
beancount = { path = "../../extensions/beancount" }
serde_json = "1"
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.6"
[dev-dependencies]
wasm-bindgen-test = "0.3.13"

Expand Down
17 changes: 17 additions & 0 deletions bindings/wasm/src/data_source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use zhang_core::data_source::{DataSource, LoadResult};
use zhang_core::data_type::DataType;
use zhang_core::ZhangResult;

pub struct InMemoryDataSource {
pub data_type: Box<dyn DataType<Carrier = String> + 'static + Send + Sync>,
}

impl DataSource for InMemoryDataSource {
fn load(&self, _entry: String, _endpoint: String) -> ZhangResult<LoadResult> {
let directive = self.data_type.transform(_entry, None)?;
Ok(LoadResult {
directives: directive,
visited_files: vec![],
})
}
}
91 changes: 88 additions & 3 deletions bindings/wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
use std::path::PathBuf;
use std::sync::Arc;

use wasm_bindgen::prelude::*;

use beancount::Beancount;
use zhang_core::data_type::text::ZhangDataType;
use zhang_core::data_type::DataType;
use zhang_core::ledger::Ledger;

use crate::data_source::InMemoryDataSource;

mod data_source;
mod utils;

// use console_error_panic_hook::hook;
Expand All @@ -12,9 +20,49 @@ mod utils;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen(getter_with_clone)]
pub struct PlayGroundParse {
pub zhang: ParseResult,
pub beancount: ParseResult,
}
#[wasm_bindgen]
impl PlayGroundParse {
pub fn zhang_parse_result(&self) -> ParseResult {
self.zhang.clone()
}

pub fn beancount_parse_result(&self) -> ParseResult {
self.beancount.clone()
}
}

#[wasm_bindgen(getter_with_clone)]
#[derive(Clone)]
pub struct ParseResult {
is_pass: bool,
msg: Option<String>,
store: Option<JsValue>,
}

#[wasm_bindgen]
impl ParseResult {
pub fn pass(&self) -> bool {
self.is_pass
}

pub fn msg(&self) -> Option<String> {
self.msg.clone()
}
pub fn store(&self) -> JsValue {
self.store.clone().unwrap_or_default()
}
}

#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}

#[wasm_bindgen]
Expand All @@ -23,8 +71,45 @@ pub fn greet() {
}

#[wasm_bindgen]
pub fn parse(content: &str) -> String {
pub fn parse(content: &str) -> PlayGroundParse {
console_error_panic_hook::set_once();
let zhang_data_type = ZhangDataType {};
let _result = zhang_data_type.transform(content.to_owned(), Some("main.zhang".to_owned()));
"OK".to_owned()
let beancount_data_type = Beancount::default();

let source = Arc::new(InMemoryDataSource {
data_type: Box::new(ZhangDataType {}),
});
let zhang_parse_result = zhang_data_type.transform(content.to_owned(), None);
let beancount_parse_result = beancount_data_type.transform(content.to_owned(), None);
let (is_zhang_pass, zhang_store, zhang_error_msg) = match zhang_parse_result {
Ok(data) => {
let result = Ledger::process(data, (PathBuf::from("/"), "".to_owned()), vec![], source.clone()).unwrap();
let result1 = result.store.read().unwrap();
let store_js_value = serde_wasm_bindgen::to_value(&*result1).unwrap();
(true, Some(store_js_value), None)
}
Err(e) => (false, None, Some(e.to_string())),
};

let (is_beancount_pass, beancount_store, beancount_error_msg) = match beancount_parse_result {
Ok(data) => {
let result = Ledger::process(data, (PathBuf::from("/"), "".to_owned()), vec![], source.clone()).unwrap();
let result1 = result.store.read().unwrap();
let store_js_value = serde_wasm_bindgen::to_value(&*result1).unwrap();
(true, Some(store_js_value), None)
}
Err(e) => (false, None, Some(e.to_string())),
};
PlayGroundParse {
zhang: ParseResult {
is_pass: is_zhang_pass,
msg: zhang_error_msg,
store: zhang_store,
},
beancount: ParseResult {
is_pass: is_beancount_pass,
msg: beancount_error_msg,
store: beancount_store,
},
}
}
38 changes: 21 additions & 17 deletions extensions/beancount/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,35 +547,39 @@ impl BeancountParer {
);
Ok(ret)
}
fn empty_space_line(input: Node) -> Result<()> {
Ok(())
}

fn item(input: Node) -> Result<(BeancountDirective, SpanInfo)> {
fn item(input: Node) -> Result<Option<(BeancountDirective, SpanInfo)>> {
let span = input.as_span();
let span_info = SpanInfo {
start: span.start_pos().pos(),
end: span.end_pos().pos(),
content: span.as_str().to_string(),
filename: None,
};
let ret: BeancountDirective = match_nodes!(input.into_children();
[option(item)] => Either::Left(item),
[plugin(item)] => Either::Left(item),
[include(item)] => Either::Left(item),
[push_tag(item)] => Either::Right(item),
[pop_tag(item)] => Either::Right(item),
[comment(item)] => Either::Left(item),
[valuable_comment(item)] => Either::Left(Directive::Comment(Comment { content:item })),

[metable_head(head)] => head,
[metable_head(head), metas(meta)] => {match head {
let ret: Option<BeancountDirective> = match_nodes!(input.into_children();
[option(item)] => Some(Either::Left(item)),
[plugin(item)] => Some(Either::Left(item)),
[include(item)] => Some(Either::Left(item)),
[push_tag(item)] => Some(Either::Right(item)),
[pop_tag(item)] => Some(Either::Right(item)),
[comment(item)] => Some(Either::Left(item)),
[valuable_comment(item)] => Some(Either::Left(Directive::Comment(Comment { content:item }))),

[empty_space_line(_)] => None,

[metable_head(head)] => Some(head),
[metable_head(head), metas(meta)] => {Some(match head {
Either::Left(directive) => Either::Left(directive.set_meta(meta)),
Either::Right(directive) => Either::Right(directive.set_meta(meta)),
}},
})},

[transaction(item)] => Either::Left(item),
[transaction(item)] => Some(Either::Left(item)),

);

Ok((ret, span_info))
Ok(ret.map(|it| (it, span_info)))
}

fn time_part(input: Node) -> Result<u32> {
Expand All @@ -591,7 +595,7 @@ impl BeancountParer {

fn entry(input: Node) -> Result<Vec<Spanned<BeancountDirective>>> {
let ret: Vec<(BeancountDirective, SpanInfo)> = match_nodes!(input.into_children();
[item(items).., _] => items.collect(),
[item(items).., _] => items.flatten().collect(),
);
Ok(ret
.into_iter()
Expand Down
25 changes: 16 additions & 9 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<img align="right" width="256" height="256" src="/docs/logo.jpg">

# 账 Zhang

a plain text double-accounting tool.
<div align="center">
<img width="256" height="256" src="/docs/logo.jpg" />
<h1>账 Zhang</h1>
<p>a plain text double-accounting tool.</p>
</div>

![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/zhang-accounting/zhang/build-latest.yml)
[![](https://codecov.io/gh/zhang-accounting/zhang/branch/main/graph/badge.svg?token=AVM0HNGF91)](https://codecov.io/gh/zhang-accounting/zhang)
Expand All @@ -11,16 +11,23 @@ a plain text double-accounting tool.
[![](https://img.shields.io/docsrs/zhang)](docs.rs/zhang)
![](https://img.shields.io/crates/l/zhang)

- Online Playground: [Online Playground](https://zhang-playground.zeabur.app/)
- Online Demo: [Online Demo](https://zhang-accounting.zeabur.app/)
- Documentation: [Documentation](https://zhang-accounting.github.io/zhang/)

## Features
- **Independent Direcitve**: all directives in zhang are independent, you can write them in any file with any order.
- **More Precise Control**: features, like commodity decimal precision and datetime supported for directive, provide more control
- **Document Management Enhancement**: zhang has a good document management feature to allow you collect and control document easiler and effective, like receipts.

- **Independent Direcitve**: all directives in zhang are independent, you can write them in any file with any order.
- **More Precise Control**: features, like commodity decimal precision and datetime supported for directive, provide
more control
- **Document Management Enhancement**: zhang has a good document management feature to allow you collect and control
document easiler and effective, like receipts.

### Compatibility with beancount

beancount and zhang are both text based accounting tools, and they are some familiar.

But zhang deprecates some directives, like `note`, `pad` and `push_tag`, and aslo evolves some direcitves, like `balance`. So your beancount file may not be compatible with zhang, we will provide a tool to convert beancount format to zhang format, and vice versa.
But zhang deprecates some directives, like `note`, `pad` and `push_tag`, and aslo evolves some direcitves,
like `balance`. So your beancount file may not be compatible with zhang, we will provide a tool to convert beancount
format to zhang format, and vice versa.

4 changes: 2 additions & 2 deletions zhang-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ itertools = "0.9"
bigdecimal = { version = "0.3", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1", features = ["v4"] }
uuid = { version = "1", features = ["v4", "serde"] }
unicode_categories = "0.1"
sha256 = "1.1.2"
sha256 = { version = "1.5.0", features = [] }
serde_json = "1"
strum = { version = "0.24", features = ["derive"] }
chrono-tz = "0.8"
Expand Down
29 changes: 16 additions & 13 deletions zhang-core/src/data_type/text/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,32 +529,35 @@ impl ZhangParser {
);
Ok(ret)
}
fn item(input: Node) -> Result<(Directive, SpanInfo)> {
fn empty_space_line(input: Node) -> Result<()> {
Ok(())
}
fn item(input: Node) -> Result<Option<(Directive, SpanInfo)>> {
let span = input.as_span();
let span_info = SpanInfo {
start: span.start_pos().pos(),
end: span.end_pos().pos(),
content: span.as_str().to_string(),
filename: None,
};
let ret: Directive = match_nodes!(input.into_children();
[option(item)] => item,
[plugin(item)] => item,
[include(item)] => item,
[valuable_comment(item)] => Directive::Comment(Comment { content:item }),

[transaction(item)] => item,

[metable_head(head)] => head,
let ret: Option<Directive> = match_nodes!(input.into_children();
[option(item)] => Some(item),
[plugin(item)] => Some(item),
[include(item)] => Some(item),
[valuable_comment(item)] => Some(Directive::Comment(Comment { content:item })),

[transaction(item)] => Some(item),
[empty_space_line(_)] => None,
[metable_head(head)] => Some(head),
[metable_head(head), metas(meta)] => {
head.set_meta(meta)
Some(head.set_meta(meta))
},
);
Ok((ret, span_info))
Ok(ret.map(|it| (it, span_info)))
}
fn entry(input: Node) -> Result<Vec<Spanned<Directive>>> {
let ret: Vec<(Directive, SpanInfo)> = match_nodes!(input.into_children();
[item(items).., _] => items.collect(),
[item(items).., _] => items.flatten().collect(),
);
Ok(ret
.into_iter()
Expand Down
2 changes: 1 addition & 1 deletion zhang-core/src/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Ledger {
Ledger::process(load_result.directives, (entry, endpoint), load_result.visited_files, data_source)
}

fn process(
pub fn process(
directives: Vec<Spanned<Directive>>, entry: (PathBuf, String), visited_files: Vec<PathBuf>, data_source: Arc<dyn DataSource>,
) -> ZhangResult<Ledger> {
let (meta_directives, dated_directive): (Vec<Spanned<Directive>>, Vec<Spanned<Directive>>) =
Expand Down
9 changes: 2 additions & 7 deletions zhang-core/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ use std::ops::{Add, Mul, Sub};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::time::Instant;

use bigdecimal::{BigDecimal, Zero};
use itertools::Itertools;
use log::debug;
use uuid::Uuid;

use zhang_ast::amount::Amount;
use zhang_ast::utils::inventory::LotInfo;
use zhang_ast::*;
Expand All @@ -24,11 +23,7 @@ use crate::ZhangResult;

pub(crate) trait DirectiveProcess {
fn handler(&mut self, ledger: &mut Ledger, span: &SpanInfo) -> ZhangResult<()> {
let start_time = Instant::now();
let result = DirectiveProcess::process(self, ledger, span);
let duration = start_time.elapsed();
debug!("directive process is done in {:?}", duration);
result
DirectiveProcess::process(self, ledger, span)
}
fn process(&mut self, ledger: &mut Ledger, span: &SpanInfo) -> ZhangResult<()>;
}
Expand Down

0 comments on commit bb0e298

Please sign in to comment.