Skip to content

Instantly share code, notes, and snippets.

@prozacchiwawa
Last active February 5, 2023 05:59
Show Gist options
  • Save prozacchiwawa/5cd35ec202ea756b106c8fbe8d28d1a1 to your computer and use it in GitHub Desktop.
Save prozacchiwawa/5cd35ec202ea756b106c8fbe8d28d1a1 to your computer and use it in GitHub Desktop.
Haskell inspired type based destructuring in rust
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}");
}
}
}
{
"texture": "foo.png",
"coord": [99, 302]
}
{
"texture": false,
"coord": [99, 302, "-9299344949223"]
}
{
"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