Initial commit

This commit is contained in:
2024-12-16 19:56:40 +01:00
commit 9905d6418f
4 changed files with 300 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

23
Cargo.lock generated Normal file
View File

@@ -0,0 +1,23 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]]
name = "json_savior"
version = "0.1.0"
dependencies = [
"json",
"lyn",
]
[[package]]
name = "lyn"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f08d9299a146aa1eb3c5451e6d9045708f232a43fd8d4436f69cf00fcb2a019c"

8
Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "json_savior"
version = "0.1.0"
edition = "2021"
[dependencies]
json = "0.12.4"
lyn = "0.1.0"

268
src/main.rs Normal file
View File

@@ -0,0 +1,268 @@
use std::io::{Read, Write};
use json::{object::Object, Array, JsonValue};
use lyn::{Action, Scanner};
fn eat_white_space(scan: &mut Scanner) {
while let Some(c) = scan.peek() {
if c.is_whitespace() {
scan.pop();
} else {
break;
}
}
}
fn eat_until_separator(scan: &mut Scanner) {
while let Some(c) = scan.peek() {
if *c == ',' || *c == '\n' || *c == ':' {
scan.pop();
break;
} else if c.is_whitespace() {
scan.pop();
} else {
break;
}
}
}
fn is_quote(c: char) -> bool {
c == '"' || c == '\''
}
fn is_value_end(c: char) -> bool {
c.is_whitespace() || c == ':' || c == ',' || c == ']' || c == '}'
}
fn scan_string(scan: &mut Scanner) -> Option<JsonValue> {
scan.scan(|input| {
let mut chars = input.chars();
let start = chars.next().unwrap();
if is_quote(start) {
if let Some(back) = chars.next_back() {
if back == start {
// Same quote type is important
return Some(Action::Return(chars.collect::<String>().into()));
}
}
// We're waiting for the end quote
Some(Action::Require)
} else if is_value_end(start) {
None
} else {
// Not a quoted string, continue until end of value
if let Some(back) = chars.next_back() {
if is_value_end(back) {
return None;
}
}
Some(Action::Request(input.into()))
}
})
.unwrap_or_default()
}
fn scan_number(scan: &mut Scanner) -> Option<JsonValue> {
scan.scan(|input| {
// Quick to write, probably horrible perf :d
let start = input.chars().next().unwrap();
if input.len() == 1 && (start == '-' || start == '+') {
// eprintln!("Parsed sign");
Some(Action::Require)
} else if let Ok(integer) = input.parse::<i32>() {
// eprintln!("Parsed {integer}");
Some(Action::Request(integer.into()))
} else if let Ok(float) = input.parse::<f32>() {
// eprintln!("Parsed {float}");
Some(Action::Request(float.into()))
} else {
// eprintln!("None on {input}");
None
}
})
// .inspect_err(|e| eprintln!("Error: {e:?}"))
.unwrap_or_default()
}
fn scan_array(scan: &mut Scanner) -> Option<JsonValue> {
if scan.take(&'[') {
let mut ret = Array::new();
while let Some(value) = scan_value(scan) {
ret.push(value);
eat_until_separator(scan);
}
eat_white_space(scan);
assert!(scan.peek().unwrap() == &']');
scan.pop();
Some(ret.into())
} else {
None
}
}
fn scan_key_value(scan: &mut Scanner) -> Option<(String, JsonValue)> {
if let Some(key) = scan_string(scan).as_ref().and_then(JsonValue::as_str) {
// eprintln!("Read key {key}");
eat_until_separator(scan);
scan_value(scan).map(|value| (key.to_owned(), value))
} else {
None
}
}
fn scan_object(scan: &mut Scanner) -> Option<JsonValue> {
if scan.take(&'{') {
let mut ret = Object::new();
eat_white_space(scan);
while let Some((key, value)) = scan_key_value(scan) {
eprintln!("Read {key} {value:?}");
ret.insert(&key, value);
eat_until_separator(scan);
eat_white_space(scan);
}
assert!(scan.peek().unwrap() == &'}');
scan.pop();
Some(ret.into())
} else {
None
}
}
fn scan_value(scan: &mut Scanner) -> Option<JsonValue> {
eat_white_space(scan);
scan_array(scan)
.or_else(|| scan_object(scan))
.or_else(|| scan_number(scan))
.or_else(|| scan_string(scan))
}
fn main() {
let mut input = String::new();
std::io::stdin().read_to_string(&mut input).unwrap();
let mut scan = Scanner::new(&input);
let out = scan_value(&mut scan).unwrap();
std::io::stdout()
.write_all(out.to_string().as_bytes())
.unwrap()
}
#[cfg(test)]
mod test {
use json::array;
use json::object::Object;
use json::Array;
use json::JsonValue;
use lyn::Scanner;
use crate::scan_array;
use crate::scan_key_value;
use crate::scan_number;
use crate::scan_object;
use crate::scan_string;
#[test]
fn test_scan_string() {
let input = "string1:string2\n\"string\",,";
let output: &[Option<&str>] = &[Some("string1"), Some("string2"), Some("string"), None];
let mut scan = Scanner::new(input);
for out in output {
assert_eq!(
scan_string(&mut scan).as_ref().and_then(JsonValue::as_str),
*out
);
scan.pop(); //skip the break char we put
}
assert!(scan.is_done());
}
#[test]
fn test_scan_number() {
let input = "13,155.12,-25,,";
let output: &[Option<f32>] = &[Some(13.), Some(155.12), Some(-25.), None];
let mut scan = Scanner::new(input);
for expected in output {
let ret = scan_number(&mut scan).as_ref().and_then(JsonValue::as_f32);
assert_eq!(ret, *expected);
scan.pop(); //skip the break char we put
}
assert!(scan.is_done());
}
#[test]
fn test_key_value() {
let input = "key:value,lol:125,,lel:,\"quoted\":'also_quoted'";
let output: &[Option<(String, JsonValue)>] = &[
Some(("key".into(), JsonValue::String("value".into()))),
Some(("lol".into(), JsonValue::Number(125.into()))),
None,
None,
Some(("quoted".into(), JsonValue::String("also_quoted".into()))),
];
let mut scan = Scanner::new(input);
for expected in output {
let ret = scan_key_value(&mut scan);
assert_eq!(ret, *expected);
scan.pop(); //skip the break char we put
}
assert!(scan.is_done());
}
#[test]
fn test_scan_array() {
let input = "[],[12, 1\n 3]\n[1],[[1],[2]],mdr";
let output: &[Option<Array>] = &[
Some(vec![]),
Some(vec![
JsonValue::Number(12.into()),
JsonValue::Number(1.into()),
JsonValue::Number(3.into()),
]),
Some(vec![JsonValue::Number(1.into())]),
Some(vec![array![1], array![2]]),
None,
];
let mut scan = Scanner::new(input);
for expected in output {
let ret = scan_array(&mut scan);
assert_eq!(
ret.is_some(),
expected.is_some(),
"Expected {expected:?}, got {ret:?}"
);
if let Some(JsonValue::Array(arr)) = ret {
assert_eq!(&arr, expected.as_ref().unwrap());
scan.pop(); //skip the break char we put
}
}
assert_eq!(scan_string(&mut scan), Some(JsonValue::from("mdr")));
assert!(scan.is_done());
}
#[test]
fn test_scan_object() {
let input = "{lol:mdr\nboloss:[megalol], \"truc\":12}";
let output: &[Option<Object>] = &[Some(Object::from_iter([
("lol", JsonValue::from("mdr")),
("boloss", array!["megalol"]),
("truc", JsonValue::from(12)),
]))];
let mut scan = Scanner::new(input);
for expected in output {
let ret = scan_object(&mut scan);
assert_eq!(
ret.is_some(),
expected.is_some(),
"Expected {expected:?}, got {ret:?}"
);
if let Some(JsonValue::Object(obj)) = ret {
assert_eq!(&obj, expected.as_ref().unwrap());
scan.pop(); //skip the break char we put
}
}
// assert_eq!(scan_string(&mut scan), Some(JsonValue::from("mdr")));
assert!(scan.is_done());
}
}