mirror of
https://github.com/tiffany352/rink-rs
synced 2024-11-10 05:34:14 +00:00
API usability improvements (#184)
* Added helper functions `load_definitions()` and `load_currency()` on Context to deduplicate code every frontend had to write out * Added ToSpans impl for `Result<QueryReply, QueryError>` to avoid a pointless looking match statement * Added more examples to the API docs * Fleshed out the API docs a bit more
This commit is contained in:
parent
df8b961edf
commit
db3f8d060b
24 changed files with 495 additions and 98 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1756,7 +1756,6 @@ dependencies = [
|
|||
"rustyline",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"similar-asserts",
|
||||
"tempfile",
|
||||
"tiny_http",
|
||||
|
@ -1796,7 +1795,6 @@ dependencies = [
|
|||
"rink-sandbox",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"toml 0.8.12",
|
||||
"ubyte",
|
||||
|
@ -1813,7 +1811,6 @@ dependencies = [
|
|||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"wee_alloc",
|
||||
|
|
|
@ -3,6 +3,6 @@ members = ["core", "rink-js", "sandbox", "cli", "irc"]
|
|||
default-members = ["cli"]
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
strip = "debuginfo"
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
|
|
@ -19,7 +19,6 @@ clap = "4.5"
|
|||
dirs = "4"
|
||||
curl = "0.4.46"
|
||||
chrono = { version = "0.4.19", default-features = false }
|
||||
serde_json = "1"
|
||||
toml = "0.5"
|
||||
serde_derive = "1"
|
||||
serde = { version = "1", default-features = false }
|
||||
|
@ -35,6 +34,7 @@ ubyte = { version = "0.10.3", features = ["serde"] }
|
|||
[dependencies.rink-core]
|
||||
version = "0.8"
|
||||
path = "../core"
|
||||
features = [ "serde_json" ]
|
||||
|
||||
[dependencies.rink-sandbox]
|
||||
version = "0.6"
|
||||
|
|
|
@ -10,7 +10,7 @@ use nu_ansi_term::{Color, Style};
|
|||
use rink_core::output::fmt::FmtToken;
|
||||
use rink_core::parsing::datetime;
|
||||
use rink_core::Context;
|
||||
use rink_core::{ast, loader::gnu_units, CURRENCY_FILE, DATES_FILE, DEFAULT_FILE};
|
||||
use rink_core::{loader::gnu_units, CURRENCY_FILE, DATES_FILE, DEFAULT_FILE};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
|
@ -279,7 +279,7 @@ pub(crate) fn force_refresh_currency(config: &Currency) -> Result<String> {
|
|||
))
|
||||
}
|
||||
|
||||
fn load_live_currency(config: &Currency) -> Result<ast::Defs> {
|
||||
fn load_live_currency(config: &Currency) -> Result<String> {
|
||||
let duration = if config.fetch_on_startup {
|
||||
Some(config.cache_duration)
|
||||
} else {
|
||||
|
@ -287,7 +287,7 @@ fn load_live_currency(config: &Currency) -> Result<ast::Defs> {
|
|||
};
|
||||
let file = cached("currency.json", &config.endpoint, duration, config.timeout)?;
|
||||
let contents = file_to_string(file)?;
|
||||
serde_json::from_str(&contents).wrap_err("Invalid JSON")
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
fn try_load_currency(config: &Currency, ctx: &mut Context, search_path: &[PathBuf]) -> Result<()> {
|
||||
|
@ -295,15 +295,9 @@ fn try_load_currency(config: &Currency, ctx: &mut Context, search_path: &[PathBu
|
|||
.into_iter()
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
let mut base_defs = gnu_units::parse_str(&base);
|
||||
let mut live_defs = load_live_currency(config)?;
|
||||
|
||||
let mut defs = vec![];
|
||||
defs.append(&mut base_defs.defs);
|
||||
defs.append(&mut live_defs.defs);
|
||||
ctx.load(ast::Defs { defs }).map_err(|err| eyre!(err))?;
|
||||
|
||||
let live_defs = load_live_currency(config)?;
|
||||
ctx.load_currency(&live_defs, &base)
|
||||
.map_err(|err| eyre!("{err}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -506,7 +500,9 @@ mod tests {
|
|||
Duration::from_millis(5),
|
||||
);
|
||||
let result = result.expect_err("this should always fail");
|
||||
assert_eq!(result.to_string(), "[28] Timeout was reached (Operation timed out after 5 milliseconds with 0 bytes received)");
|
||||
let result = result.to_string();
|
||||
assert!(result.starts_with("[28] Timeout was reached (Operation timed out after "));
|
||||
assert!(result.ends_with(" milliseconds with 0 bytes received)"));
|
||||
thread_handle.join().unwrap();
|
||||
drop(server);
|
||||
}
|
||||
|
@ -547,7 +543,7 @@ mod tests {
|
|||
let thread_handle = std::thread::spawn(move || {
|
||||
let request = server2.recv().expect("the request should not fail");
|
||||
assert_eq!(request.url(), "/data/currency.json");
|
||||
let mut data = b"{}".to_owned();
|
||||
let mut data = include_bytes!("../../core/tests/currency.snapshot.json").to_owned();
|
||||
let cursor = std::io::Cursor::new(&mut data);
|
||||
request
|
||||
.respond(Response::new(StatusCode(200), vec![], cursor, None, None))
|
||||
|
@ -563,7 +559,10 @@ mod tests {
|
|||
result
|
||||
.read_to_string(&mut string)
|
||||
.expect("the file should exist");
|
||||
assert_eq!(string, "{}");
|
||||
assert_eq!(
|
||||
string,
|
||||
include_str!("../../core/tests/currency.snapshot.json")
|
||||
);
|
||||
thread_handle.join().unwrap();
|
||||
drop(server);
|
||||
}
|
||||
|
@ -584,7 +583,7 @@ mod tests {
|
|||
let thread_handle = std::thread::spawn(move || {
|
||||
let request = server2.recv().expect("the request should not fail");
|
||||
assert_eq!(request.url(), "/data/currency.json");
|
||||
let mut data = b"{}".to_owned();
|
||||
let mut data = include_bytes!("../../core/tests/currency.snapshot.json").to_owned();
|
||||
let cursor = std::io::Cursor::new(&mut data);
|
||||
request
|
||||
.respond(Response::new(StatusCode(200), vec![], cursor, None, None))
|
||||
|
@ -592,7 +591,7 @@ mod tests {
|
|||
});
|
||||
let result = super::force_refresh_currency(&config);
|
||||
let result = result.expect("this should succeed");
|
||||
assert!(result.starts_with("Fetched 2 byte currency file after "));
|
||||
assert!(result.starts_with("Fetched 6599 byte currency file after "));
|
||||
thread_handle.join().unwrap();
|
||||
drop(server);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ edition = "2018"
|
|||
[features]
|
||||
default = ["chrono-humanize"]
|
||||
bundle-files = []
|
||||
serde_json = ["dep:serde_json"]
|
||||
|
||||
[dependencies]
|
||||
num-bigint = { version = "0.4", features = ["serde"] }
|
||||
|
@ -22,6 +23,7 @@ strsim = "0.10.0"
|
|||
chrono-tz = { version = "0.5.2", default-features = false }
|
||||
chrono-humanize = { version = "0.1.2", optional = true }
|
||||
serde = { version = "1", features = ["rc"], default-features = false }
|
||||
serde_json = { version = "1", optional = true }
|
||||
serde_derive = "1"
|
||||
indexmap = "2"
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! Abstract syntax tree for rink's query language.
|
||||
|
||||
use crate::output::Digits;
|
||||
use crate::types::Numeric;
|
||||
use chrono_tz::Tz;
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! Provides direct access to commands that can be run in rink, like
|
||||
//! [search()] and [factorize()].
|
||||
|
||||
mod factorize;
|
||||
mod search;
|
||||
|
||||
|
|
|
@ -3,27 +3,50 @@
|
|||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::{
|
||||
loader::gnu_units,
|
||||
output::{QueryError, QueryReply},
|
||||
parsing::text_query,
|
||||
Context,
|
||||
};
|
||||
|
||||
/// The default `definitions.units` file that contains all of the base
|
||||
/// units, units, prefixes, quantities, and substances.
|
||||
///
|
||||
/// This will be Some if the `bundle-files` feature is enabled,
|
||||
/// otherwise it will be None.
|
||||
#[cfg(feature = "bundle-files")]
|
||||
pub static DEFAULT_FILE: Option<&'static str> = Some(include_str!("../definitions.units"));
|
||||
#[cfg(not(feature = "bundle-files"))]
|
||||
pub static DEFAULT_FILE: Option<&'static str> = None;
|
||||
|
||||
/// The default `datepatterns.txt` file that contains patterns that rink
|
||||
/// uses for parsing datetimes.
|
||||
///
|
||||
/// This will be Some if the `bundle-files` feature is enabled,
|
||||
/// otherwise it will be None.
|
||||
#[cfg(feature = "bundle-files")]
|
||||
pub static DATES_FILE: Option<&'static str> = Some(include_str!("../datepatterns.txt"));
|
||||
#[cfg(not(feature = "bundle-files"))]
|
||||
pub static DATES_FILE: Option<&'static str> = None;
|
||||
|
||||
/// The default `currenty.units` file that contains currency information
|
||||
/// that changes rarely. It's used together with live currency data
|
||||
/// to add currency support to rink.
|
||||
///
|
||||
/// This will be Some if the `bundle-files` feature is enabled,
|
||||
/// otherwise it will be None.
|
||||
#[cfg(feature = "bundle-files")]
|
||||
pub static CURRENCY_FILE: Option<&'static str> = Some(include_str!("../currency.units"));
|
||||
#[cfg(not(feature = "bundle-files"))]
|
||||
pub static CURRENCY_FILE: Option<&'static str> = None;
|
||||
|
||||
/// Helper function that updates the `now` to the current time, parses
|
||||
/// the query, evaluates it, and updates the `previous_result` field
|
||||
/// that's used to return the previous query when using `ans`.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// Panics on platforms where fetching the current time is not possible,
|
||||
/// such as WASM.
|
||||
pub fn eval(ctx: &mut Context, line: &str) -> Result<QueryReply, QueryError> {
|
||||
ctx.update_time();
|
||||
let mut iter = text_query::TokenIterator::new(line.trim()).peekable();
|
||||
|
@ -39,7 +62,7 @@ pub fn eval(ctx: &mut Context, line: &str) -> Result<QueryReply, QueryError> {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
/// A version of eval() that converts results and errors into strings.
|
||||
/// A version of eval() that converts results and errors into plain-text strings.
|
||||
pub fn one_line(ctx: &mut Context, line: &str) -> Result<String, String> {
|
||||
eval(ctx, line)
|
||||
.as_ref()
|
||||
|
@ -54,20 +77,16 @@ pub fn simple_context() -> Result<Context, String> {
|
|||
let message = "bundle-files feature not enabled, cannot create simple context.";
|
||||
|
||||
let units = DEFAULT_FILE.ok_or(message.to_owned())?;
|
||||
let mut iter = gnu_units::TokenIterator::new(&*units).peekable();
|
||||
let units = gnu_units::parse(&mut iter);
|
||||
|
||||
let dates = DATES_FILE.ok_or(message.to_owned())?;
|
||||
let dates = crate::parsing::datetime::parse_datefile(dates);
|
||||
|
||||
let mut ctx = Context::new();
|
||||
ctx.load(units)?;
|
||||
ctx.load_dates(dates);
|
||||
ctx.load_definitions(units)?;
|
||||
ctx.load_date_file(dates);
|
||||
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
// Returns `env!("CARGO_PKG_VERSION")`, a string in `x.y.z` format.
|
||||
/// Returns `env!("CARGO_PKG_VERSION")`, a string in `x.y.z` format.
|
||||
pub fn version() -> &'static str {
|
||||
env!("CARGO_PKG_VERSION")
|
||||
}
|
||||
|
|
120
core/src/lib.rs
120
core/src/lib.rs
|
@ -2,32 +2,100 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
/*! The primary interface of this library is meant to expose a very
|
||||
simple command-reply model for frontends, and to allow gradual
|
||||
addition of more advanced functionality. For now, only basic
|
||||
functionality exists.
|
||||
|
||||
Using Rink as a library for uses other than simple unit conversion
|
||||
tools is not currently well supported, and if you wish to do so,
|
||||
please make issues for any problems you have.
|
||||
|
||||
There are currently a number of hardcoded `println!`s and `unwrap()`s
|
||||
because most of this code was written in a day without much thought
|
||||
towards making it into a library.
|
||||
|
||||
To use the library, check how the CLI tool does it. To get additional
|
||||
features like currency and BTC you'll need to fetch those files
|
||||
yourself and add them into the Context.
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use rink_core::*;
|
||||
|
||||
let mut ctx = simple_context().unwrap();
|
||||
println!("{}", one_line(&mut ctx, "kWh / year -> W").unwrap());
|
||||
```
|
||||
*/
|
||||
//! Rink is a small language for calculations and unit conversions.
|
||||
//! It is available as a CLI, a web interface, an IRC client.
|
||||
//! `rink_core` is the library that the frontends use.
|
||||
//!
|
||||
//! The API is designed to let you start simple and then progressively
|
||||
//! add more features.
|
||||
//!
|
||||
//! Rink is designed to be used interactively, with the user typing a
|
||||
//! query and then seeing the result. It's common for this to be a
|
||||
//! session, so the previous query can be referenced with `ans`, and
|
||||
//! some form of history is available using up/down arrows.
|
||||
//!
|
||||
//! Using rink for purposes other than this is out of scope, but may be
|
||||
//! possible anyway depending on what you're trying to do.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! Minimal implementation.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # fn main() -> Result<(), String> {
|
||||
//! // Create a context. This is expensive (30+ ms), so do it once at
|
||||
//! // startup and keep it around.
|
||||
//! let mut ctx = rink_core::simple_context()?;
|
||||
//! // `one_line` is a helper function that parses a query, evaluates
|
||||
//! // it, then converts the result into a plain text string.
|
||||
//! println!("{}", rink_core::one_line(&mut ctx, "kWh / year -> W")?);
|
||||
//! // Prints: approx. 0.1140795 watt (power)
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Currency fetching
|
||||
//!
|
||||
//! The first step to adding currency fetching is to add code to
|
||||
//! download this file:
|
||||
//!
|
||||
//! <https://rinkcalc.app/data/currency.json>
|
||||
//!
|
||||
//! You can use any http library, such as `curl` or `reqwest`. The file
|
||||
//! updates about once an hour. Please make sure to set an accurate
|
||||
//! user-agent when fetching it.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # fn fetch_from_http(_url: &str) -> String { include_str!("../tests/currency.snapshot.json").to_owned() }
|
||||
//! # fn main() -> Result<(), String> {
|
||||
//! # let mut ctx = rink_core::simple_context()?;
|
||||
//! let live_data: String = fetch_from_http("https://rinkcalc.app/data/currency.json");
|
||||
//! // CURRENCY_FILE requires that the `bundle-features` feature is
|
||||
//! // enabled. Otherwise, you'll need to install and load this file
|
||||
//! // yourself.
|
||||
//! let base_defs = rink_core::CURRENCY_FILE.expect("bundle-files feature to be enabled");
|
||||
//! ctx.load_currency(&live_data, base_defs)?;
|
||||
//!
|
||||
//! println!("{}", rink_core::one_line(&mut ctx, "USD").unwrap());
|
||||
//! // Definition: USD = (1 / 1.0843) EUR = approx. 922.2539 millieuro (money; EUR).
|
||||
//! // Sourced from European Central Bank. Current as of 2024-05-27.
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Markup
|
||||
//!
|
||||
//! To add color highlighting, or other forms of rich markup such as
|
||||
//! links or superscripts, you can use [eval] instead of [one_line] and
|
||||
//! then call [output::fmt::TokenFmt::to_spans] on the result. This
|
||||
//! returns a tree of spans, each of which has a formatting hint
|
||||
//! attached to it. See [output::fmt::Span] and [output::fmt::FmtToken].
|
||||
//!
|
||||
//! ```rust
|
||||
//! use rink_core::output::fmt::{TokenFmt, Span, FmtToken};
|
||||
//! # let mut ctx = rink_core::simple_context().unwrap();
|
||||
//! let result = rink_core::eval(&mut ctx, "meter");
|
||||
//! // converts both the Ok and Err cases to spans
|
||||
//! let spans = result.to_spans();
|
||||
//!
|
||||
//! fn write_xml(out: &mut String, spans: &[Span]) {
|
||||
//! for span in spans {
|
||||
//! match span {
|
||||
//! Span::Content {text, token: FmtToken::DocString} => {
|
||||
//! out.push_str("<i>");
|
||||
//! out.push_str(&text);
|
||||
//! out.push_str("</i>");
|
||||
//! }
|
||||
//! Span::Content {text, ..} => out.push_str(&text),
|
||||
//! Span::Child(child) => write_xml(out, &child.to_spans()),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let mut out = String::new();
|
||||
//! write_xml(&mut out, &spans);
|
||||
//! println!("{}", out);
|
||||
//! ```
|
||||
|
||||
// False positives, or make code harder to understand.
|
||||
#![allow(clippy::cognitive_complexity)]
|
||||
|
|
|
@ -65,6 +65,11 @@ impl Context {
|
|||
self.registry.datepatterns.append(&mut dates)
|
||||
}
|
||||
|
||||
pub fn load_date_file(&mut self, file: &str) {
|
||||
let dates = crate::parsing::datetime::parse_datefile(file);
|
||||
self.load_dates(dates)
|
||||
}
|
||||
|
||||
/// Given a unit name, returns its value if it exists. Supports SI
|
||||
/// prefixes, plurals, bare dimensions like length, and quantities.
|
||||
pub fn lookup(&self, name: &str) -> Option<Number> {
|
||||
|
@ -196,6 +201,33 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
// Takes the string definition.units file, parses it, and loads it.
|
||||
pub fn load_definitions(&mut self, content: &str) -> Result<(), String> {
|
||||
let defs = crate::loader::gnu_units::parse_str(&content);
|
||||
self.load(defs)
|
||||
}
|
||||
|
||||
// Takes the currency JSON and the currency.units file, parses both,
|
||||
// and loads them.
|
||||
//
|
||||
// The latest live_data string can be obtained by making a web
|
||||
// request to: https://rinkcalc.app/data/currency.json
|
||||
//
|
||||
// The currency.unit file exists at rink_core::CURRENCY_FILE when
|
||||
// the `bundle-files` feature is enabled. Otherwise, it needs to be
|
||||
// installed on the system somewhere and loaded.
|
||||
#[cfg(feature = "serde_json")]
|
||||
pub fn load_currency(&mut self, live_data: &str, currency_units: &str) -> Result<(), String> {
|
||||
let mut base_defs = crate::loader::gnu_units::parse_str(currency_units);
|
||||
let mut live_defs: Vec<crate::ast::DefEntry> =
|
||||
serde_json::from_str(&live_data).map_err(|err| format!("{}", err))?;
|
||||
|
||||
let mut defs = vec![];
|
||||
defs.append(&mut base_defs.defs);
|
||||
defs.append(&mut live_defs);
|
||||
self.load(crate::ast::Defs { defs })
|
||||
}
|
||||
|
||||
/// Evaluates an expression to compute its value, *excluding* `->`
|
||||
/// conversions.
|
||||
pub fn eval(&self, expr: &Expr) -> Result<Value, QueryError> {
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! [Context], [Registry], and the [definitions file parser][gnu_units]
|
||||
|
||||
mod context;
|
||||
pub mod gnu_units;
|
||||
mod load;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! Return types from evaluating a query, starting from [QueryReply] and [QueryError]
|
||||
|
||||
mod doc_string;
|
||||
pub mod fmt;
|
||||
mod number_parts;
|
||||
|
|
|
@ -640,3 +640,12 @@ impl<'a> TokenFmt<'a> for NotFoundError {
|
|||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TokenFmt<'a> for Result<QueryReply, QueryError> {
|
||||
fn to_spans(&'a self) -> Vec<Span<'a>> {
|
||||
match self {
|
||||
Ok(res) => res.to_spans(),
|
||||
Err(err) => err.to_spans(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! Parsers for [rink's query language][text_query], [molecular
|
||||
//! formulas][formula::substance_from_formula], and [datetimes][datetime].
|
||||
|
||||
pub mod datetime;
|
||||
pub mod formula;
|
||||
pub mod text_query;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! [Value] stores all types that rink is capable of processing
|
||||
|
||||
mod eval;
|
||||
mod substance;
|
||||
mod value;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! Basic types that rink uses, such as [arbitrary-precision rationals][BigRat]
|
||||
|
||||
mod base_unit;
|
||||
mod bigint;
|
||||
mod bigrat;
|
||||
|
|
269
core/tests/currency.snapshot.json
Normal file
269
core/tests/currency.snapshot.json
Normal file
|
@ -0,0 +1,269 @@
|
|||
[
|
||||
{
|
||||
"name": "BTC",
|
||||
"doc": null,
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "price of bitcoin"
|
||||
},
|
||||
{
|
||||
"name": "bitcoin",
|
||||
"doc": "Properties of the global Bitcoin network. Sourced from <https://blockchain.info>. Current as of Sun, 02 Jun 2024 21:38:26 GMT",
|
||||
"category": "currencies",
|
||||
"type": "substance",
|
||||
"symbol": null,
|
||||
"properties": [
|
||||
{
|
||||
"name": "price",
|
||||
"doc": "Current market price of 1 BTC.",
|
||||
"category": "currencies",
|
||||
"inputName": "bitcoin",
|
||||
"input": "1",
|
||||
"outputName": "bitcoin",
|
||||
"output": "67710.94 USD"
|
||||
},
|
||||
{
|
||||
"name": "hashrate",
|
||||
"doc": "Current hash rate of the global network",
|
||||
"category": null,
|
||||
"inputName": "hashrate",
|
||||
"input": "1",
|
||||
"outputName": "rate",
|
||||
"output": "562079779113.5204 1e9 'hash'/sec"
|
||||
},
|
||||
{
|
||||
"name": "total",
|
||||
"doc": "Total number of BTC in circulation.",
|
||||
"category": null,
|
||||
"inputName": "total",
|
||||
"input": "1",
|
||||
"outputName": "bitcoin",
|
||||
"output": "1970701250000000 / 1e8"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "USD",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 1.0852) EUR"
|
||||
},
|
||||
{
|
||||
"name": "JPY",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 170.52) EUR"
|
||||
},
|
||||
{
|
||||
"name": "BGN",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 1.9558) EUR"
|
||||
},
|
||||
{
|
||||
"name": "CZK",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 24.709) EUR"
|
||||
},
|
||||
{
|
||||
"name": "DKK",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 7.4588) EUR"
|
||||
},
|
||||
{
|
||||
"name": "GBP",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 0.85365) EUR"
|
||||
},
|
||||
{
|
||||
"name": "HUF",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 388.83) EUR"
|
||||
},
|
||||
{
|
||||
"name": "PLN",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 4.2645) EUR"
|
||||
},
|
||||
{
|
||||
"name": "RON",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 4.9767) EUR"
|
||||
},
|
||||
{
|
||||
"name": "SEK",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 11.4210) EUR"
|
||||
},
|
||||
{
|
||||
"name": "CHF",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 0.9818) EUR"
|
||||
},
|
||||
{
|
||||
"name": "ISK",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 149.10) EUR"
|
||||
},
|
||||
{
|
||||
"name": "NOK",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 11.3830) EUR"
|
||||
},
|
||||
{
|
||||
"name": "TRY",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 34.9691) EUR"
|
||||
},
|
||||
{
|
||||
"name": "AUD",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 1.6315) EUR"
|
||||
},
|
||||
{
|
||||
"name": "BRL",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 5.6418) EUR"
|
||||
},
|
||||
{
|
||||
"name": "CAD",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 1.4804) EUR"
|
||||
},
|
||||
{
|
||||
"name": "CNY",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 7.8577) EUR"
|
||||
},
|
||||
{
|
||||
"name": "HKD",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 8.4838) EUR"
|
||||
},
|
||||
{
|
||||
"name": "IDR",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 17641.72) EUR"
|
||||
},
|
||||
{
|
||||
"name": "ILS",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 4.0342) EUR"
|
||||
},
|
||||
{
|
||||
"name": "INR",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 90.5255) EUR"
|
||||
},
|
||||
{
|
||||
"name": "KRW",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 1501.11) EUR"
|
||||
},
|
||||
{
|
||||
"name": "MXN",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 18.4387) EUR"
|
||||
},
|
||||
{
|
||||
"name": "MYR",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 5.1080) EUR"
|
||||
},
|
||||
{
|
||||
"name": "NZD",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 1.7696) EUR"
|
||||
},
|
||||
{
|
||||
"name": "PHP",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 63.604) EUR"
|
||||
},
|
||||
{
|
||||
"name": "SGD",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 1.4663) EUR"
|
||||
},
|
||||
{
|
||||
"name": "THB",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 39.925) EUR"
|
||||
},
|
||||
{
|
||||
"name": "ZAR",
|
||||
"doc": "Sourced from European Central Bank. Current as of 2024-05-31.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 20.2927) EUR"
|
||||
},
|
||||
{
|
||||
"name": "HRK",
|
||||
"doc": "Croatian Kuna. Pinned to Euro at a fixed rate since 2023-01-01.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 7.5345) EUR"
|
||||
},
|
||||
{
|
||||
"name": "RUB",
|
||||
"doc": "Fetching live data failed. Fallback value provided from xe.com on 2024-01-06.",
|
||||
"category": "currencies",
|
||||
"type": "unit",
|
||||
"expr": "(1 / 99.477867) EUR"
|
||||
}
|
||||
]
|
17
core/tests/currency_loading.rs
Normal file
17
core/tests/currency_loading.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
#[test]
|
||||
fn load_currency() {
|
||||
let mut ctx = rink_core::simple_context().unwrap();
|
||||
let live_data = include_str!("../tests/currency.snapshot.json");
|
||||
let base_defs = rink_core::CURRENCY_FILE.unwrap();
|
||||
ctx.load_currency(&live_data, base_defs).unwrap();
|
||||
|
||||
let result = rink_core::one_line(&mut ctx, "USD");
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok("Definition: USD = (1 / 1.0852) EUR = \
|
||||
approx. 921.4891 millieuro (money; EUR). \
|
||||
Sourced from European Central Bank. \
|
||||
Current as of 2024-05-31."
|
||||
.to_owned())
|
||||
);
|
||||
}
|
|
@ -63,10 +63,7 @@ fn test(input: &str, output: &[FlatSpan<'static>]) {
|
|||
let expr = text_query::parse_query(&mut iter);
|
||||
CONTEXT.with(|ctx| {
|
||||
let res = ctx.eval_query(&expr);
|
||||
let res = match res {
|
||||
Ok(ref v) => v.to_spans(),
|
||||
Err(ref v) => v.to_spans(),
|
||||
};
|
||||
let res = res.to_spans();
|
||||
let res = res.into_iter().map(FlatSpan::from).collect::<Vec<_>>();
|
||||
similar_asserts::assert_eq!(res, output);
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ edition = "2018"
|
|||
[dependencies.rink-core]
|
||||
version = "0.8"
|
||||
path = "../core"
|
||||
features = [ "bundle-files" ]
|
||||
features = [ "bundle-files", "serde_json" ]
|
||||
|
||||
[dependencies.rink-sandbox]
|
||||
path = "../sandbox"
|
||||
|
@ -22,7 +22,6 @@ humantime-serde = "1.0.1"
|
|||
irc = "1.0.0"
|
||||
serde = { version = "1", default-features = false }
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
toml = "0.8"
|
||||
ubyte = { version = "0.10.3", features = ["serde"] }
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
use irc::client::data::Config as IrcConfig;
|
||||
use rink_core::{ast, loader::gnu_units, parsing::datetime, Context};
|
||||
use rink_core::{parsing::datetime, Context};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
use ubyte::ByteUnit;
|
||||
|
@ -76,13 +76,7 @@ fn try_load_currency(config: &Currency, ctx: &mut Context) {
|
|||
let base = rink_core::CURRENCY_FILE.unwrap();
|
||||
let live = std::fs::read_to_string(&config.path).unwrap();
|
||||
|
||||
let mut base_defs = gnu_units::parse_str(&base);
|
||||
let mut live_defs: ast::Defs = serde_json::from_str(&live).unwrap();
|
||||
|
||||
let mut defs = vec![];
|
||||
defs.append(&mut base_defs.defs);
|
||||
defs.append(&mut live_defs.defs);
|
||||
ctx.load(ast::Defs { defs }).unwrap();
|
||||
ctx.load_currency(&live, &base).unwrap();
|
||||
}
|
||||
|
||||
/// Creates a context by searching standard directories
|
||||
|
@ -94,7 +88,7 @@ pub fn load(config: &Config) -> Context {
|
|||
|
||||
let mut ctx = Context::new();
|
||||
ctx.save_previous_result = true;
|
||||
ctx.load(gnu_units::parse_str(&units)).unwrap();
|
||||
ctx.load_definitions(&units).unwrap();
|
||||
ctx.load_dates(datetime::parse_datefile(&dates));
|
||||
|
||||
// Load currency data.
|
||||
|
|
|
@ -42,10 +42,7 @@ async fn server_task(config: config::Config, index: usize) {
|
|||
};
|
||||
println!("[{servername}] <== {command}");
|
||||
let result = rink_core::eval(&mut ctx, command);
|
||||
let result = match &result {
|
||||
Ok(res) => res.to_spans(),
|
||||
Err(err) => err.to_spans(),
|
||||
};
|
||||
let result = result.to_spans();
|
||||
let result = fmt::to_irc_string(&config, &result);
|
||||
println!("[{servername}] ==> {result}");
|
||||
let where_to = if channel == client.current_nickname() {
|
||||
|
|
|
@ -17,7 +17,7 @@ default = ["console_error_panic_hook"]
|
|||
[dependencies.rink-core]
|
||||
path = "../core"
|
||||
version = "0.8"
|
||||
features = ["bundle-files"]
|
||||
features = ["bundle-files", "serde_json"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = { version = "0.2", default-features = false }
|
||||
|
@ -26,7 +26,6 @@ wee_alloc = { version = "0.4.5", default-features = false }
|
|||
chrono = { version = "0.4.13", default-features = false }
|
||||
serde = { version = "1", default-features = false }
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
serde-wasm-bindgen = "0.6"
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
|
|
|
@ -123,22 +123,8 @@ impl Context {
|
|||
|
||||
#[wasm_bindgen(js_name = loadCurrency)]
|
||||
pub fn load_currency(&mut self, live_defs: String) -> Result<(), JsValue> {
|
||||
let mut live_defs: Vec<ast::DefEntry> =
|
||||
serde_json::from_str(&live_defs).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut base_defs = {
|
||||
use rink_core::loader::gnu_units;
|
||||
let defs = rink_core::CURRENCY_FILE.unwrap();
|
||||
let mut iter = gnu_units::TokenIterator::new(defs).peekable();
|
||||
gnu_units::parse(&mut iter)
|
||||
};
|
||||
let currency = {
|
||||
let mut defs = vec![];
|
||||
defs.append(&mut live_defs);
|
||||
defs.append(&mut base_defs.defs);
|
||||
ast::Defs { defs }
|
||||
};
|
||||
self.context.load(currency)?;
|
||||
let base_defs = rink_core::CURRENCY_FILE.unwrap();
|
||||
self.context.load_currency(&live_defs, base_defs)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -170,10 +156,7 @@ impl Context {
|
|||
}
|
||||
}
|
||||
}
|
||||
let spans = match value {
|
||||
Ok(ref value) => value.to_spans(),
|
||||
Err(ref value) => value.to_spans(),
|
||||
};
|
||||
let spans = value.to_spans();
|
||||
let tokens = visit_tokens(&spans);
|
||||
|
||||
match serde_wasm_bindgen::to_value(&tokens) {
|
||||
|
|
Loading…
Reference in a new issue