Last active
February 5, 2023 05:59
-
-
Save prozacchiwawa/5cd35ec202ea756b106c8fbe8d28d1a1 to your computer and use it in GitHub Desktop.
Haskell inspired type based destructuring in rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use serde_json; | |
pub enum And<T,U> { | |
These(T,U) | |
} | |
pub enum Field<T> { | |
Named(&'static str,T) | |
} | |
pub enum Idx<T> { | |
At(usize,T) | |
} | |
pub enum Integer { | |
Here, | |
} | |
pub enum Str { | |
Here, | |
} | |
pub trait SelectNode<T, E> where E: Default { | |
fn choose(&self, v: &serde_json::Value) -> Result<T,E>; | |
} | |
impl<E> SelectNode<String,E> for Str where E: Default { | |
fn choose(&self, v: &serde_json::Value) -> Result<String,E> { | |
match v { | |
serde_json::Value::String(s) => { | |
Ok(s.to_string()) | |
} | |
serde_json::Value::Bool(b) => { | |
Ok(b.to_string()) | |
} | |
_ => Err(Default::default()) | |
} | |
} | |
} | |
impl<E> SelectNode<i64,E> for Integer where E: Default { | |
fn choose(&self, v: &serde_json::Value) -> Result<i64,E> { | |
match v { | |
serde_json::Value::String(s) => { | |
s.parse().map_err(|_| Default::default()) | |
} | |
serde_json::Value::Number(n) => { | |
n.as_i64().map(Ok).unwrap_or(Err(Default::default())) | |
} | |
_ => Err(Default::default()) | |
} | |
} | |
} | |
impl<R, T, E> SelectNode<T,E> for Idx<R> | |
where | |
E: Default, | |
R: SelectNode<T,E> | |
{ | |
fn choose(&self, v: &serde_json::Value) -> Result<T, E> { | |
let Idx::At(i,selector) = self; | |
if let serde_json::Value::Array(v) = v { | |
if *i >= v.len() { | |
return Err(Default::default()); | |
} | |
return selector.choose(&v[*i]); | |
} | |
Err(Default::default()) | |
} | |
} | |
impl<R, T, E> SelectNode<T,E> for Field<R> | |
where | |
E: Default, | |
R: SelectNode<T,E> | |
{ | |
fn choose(&self, v: &serde_json::Value) -> Result<T, E> { | |
let Field::Named(name, selector) = self; | |
if let serde_json::Value::Object(map) = v { | |
if let Some(found) = map.get(&name.to_string()) { | |
return selector.choose(found); | |
} | |
} | |
Err(Default::default()) | |
} | |
} | |
impl<R,S,T,U,E> SelectNode<And<T,U>,E> for And<R,S> | |
where | |
E: Default, | |
R: SelectNode<T,E>, | |
S: SelectNode<U,E> | |
{ | |
fn choose(&self, v: &serde_json::Value) -> Result<And<T,U>, E> { | |
let And::These(a,b) = self; | |
let first = a.choose(v)?; | |
let second = b.choose(v)?; | |
Ok(And::These(first,second)) | |
} | |
} | |
#[derive(Debug)] | |
struct PickedOutData { | |
pub x: i64, | |
pub y: i64, | |
pub z: i64, | |
pub texture: String | |
} | |
fn do_match_json(v: &serde_json::Value) -> Result<PickedOutData, ()> { | |
let And::These( | |
texture, | |
And::These( | |
x, | |
And::These( | |
y, | |
z | |
) | |
)) | |
= And::These( | |
Field::Named("texture", Str::Here), | |
Field::Named( | |
"coord", | |
And::These( | |
Idx::At(0, Integer::Here), | |
And::These( | |
Idx::At(1, Integer::Here), | |
Idx::At(2, Integer::Here) | |
) | |
) | |
) | |
).choose(v)?; | |
Ok(PickedOutData { x, y, z, texture }) | |
} | |
fn main() { | |
for a in std::env::args().skip(1) { | |
let s = std::fs::read_to_string(&a).expect("should read"); | |
let parsed = serde_json::from_str(&s).expect("should parse"); | |
if let Ok(res) = do_match_json(&parsed) { | |
println!("-- {a} --"); | |
println!("texture = {}, x = {}, y = {}, z = {}", | |
res.texture, | |
res.x, | |
res.y, | |
res.z | |
); | |
} else { | |
eprintln!("didn't match {a}"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"texture": "foo.png", | |
"coord": [99, 302] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"texture": false, | |
"coord": [99, 302, "-9299344949223"] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"texture": "foo.png", | |
"coord": [99, 302, "-9299344949223"] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment