Skip to content

Commit

Permalink
add new macros
Browse files Browse the repository at this point in the history
  • Loading branch information
stackdump committed Sep 14, 2024
1 parent d3bcb2e commit eea128a
Show file tree
Hide file tree
Showing 12 changed files with 714 additions and 219 deletions.
23 changes: 21 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pflow-metamodel"
version = "0.1.2"
version = "0.2.0"
edition = "2021"
description = "Declarative Petri-nets using a rust DSL"
license = "MIT"
Expand All @@ -17,6 +17,7 @@ path = "src/lib.rs"
base64 = "0.21.7"
brotli = "3.4.0"
cjson = "0.1.2"
embed-doc-image = "0.1.4"
libipld = "0.16.0"
multibase = "0.9.1"
serde = { version = "1.0", features = ["derive"] }
Expand Down
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CLIPPY_ARGS=-Dwarnings -Drust-2018-idioms -Drust-2021-compatibility -Adeprecated

.PHONY: clippy
clippy:
cargo clippy --fix --allow-dirty --all --all-targets --all-features -- $(CLIPPY_ARGS)

.PHONY: test
test:
cargo test --all
52 changes: 24 additions & 28 deletions src/compression.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,61 @@
use std::io::Cursor;
use std::io::Read;
use std::io::Write;
use base64::{Engine as _, engine::general_purpose};


use base64::{engine::general_purpose, Engine as _};
use std::io::{Cursor, Read, Write};
use brotli::CompressorWriter;
use std::error::Error;


pub fn decompress_brotli_decode(encoded_data: &str) -> Option<String> {
let decoded = general_purpose::STANDARD.decode(encoded_data);
if !decoded.is_ok() {
return None
}
/// Decompresses the given brotli encoded string.
pub fn decompress_brotli_decode(encoded_data: &str) -> Result<String, Box<dyn Error>> {
let decoded = general_purpose::STANDARD.decode(encoded_data)?;
let mut decompressed_data = Vec::new();
let mut decompressor = brotli::Decompressor::new(Cursor::new(decoded.unwrap()), 4096);
decompressor.read_to_end(&mut decompressed_data).unwrap();
Option::from(String::from_utf8(decompressed_data).unwrap())
let mut decompressor = brotli::Decompressor::new(Cursor::new(decoded), 4096);
decompressor.read_to_end(&mut decompressed_data)?;
Ok(String::from_utf8(decompressed_data)?)
}

/// Decodes the base64 string from the given URL into a zip file and then extracts the file with the given filename from the zip file.
pub fn decompress_encoded_url(url: &str) -> Option<String> {
pub fn decompress_encoded_url(url: &str) -> Result<String, Box<dyn Error>> {
let query_string = url.split('?').collect::<Vec<&str>>()[1];
let z = query_string
.split('&')
.find(|&param| param.starts_with("z="))?;
let z = &z[2..];
.find(|&param| param.starts_with("z="))
.ok_or("failed to extract")?;

let z = &z[2..];
decompress_brotli_decode(z)
}

pub fn compress_brotli_encode(data: &str) -> String {
/// Compresses the given string using brotli encoding and then encodes it in base64.
pub fn compress_brotli_encode(data: &str) -> Result<String, Box<dyn Error>> {
let mut compressed_data = Vec::new();
{
let mut compressor = CompressorWriter::new(&mut compressed_data, 4096, 5, 22); // 4096 is the buffer size, 5 is quality, 22 is lgwin
compressor.write_all(data.as_bytes()).unwrap();
compressor.write_all(data.as_bytes())?;
} // CompressorWriter is flushed and finished when it goes out of scope

general_purpose::STANDARD.encode(compressed_data)
Ok(general_purpose::STANDARD.encode(compressed_data))
}

#[cfg(test)]
mod tests {
use crate::fixtures::{DINING_PHILOSOPHERS};
use crate::fixtures::DINING_PHILOSOPHERS;
use crate::petri_net::PetriNet;

use super::*;

#[test]
fn test_unzip_base64_encoded() {
let encoded = compress_brotli_encode(DINING_PHILOSOPHERS);
println!("encoded: http://localhost:3000/?z={:}", encoded);
let decoded = decompress_brotli_decode(&encoded).unwrap();
let encoded = compress_brotli_encode(DINING_PHILOSOPHERS).expect("Compression failed");
println!("encoded: http://localhost:3000/?z={encoded:}");
let decoded = decompress_brotli_decode(&encoded).expect("Decompression failed");
assert_eq!(decoded, DINING_PHILOSOPHERS);
}

#[test]
fn test_unzip_url() {
let url = "http://localhost:3000/?z=GzkCIBwHdqMPWUYyo7XgaT/B09w+1fHywu1u31IMRQwiCxaRsTAxQRT6UodF4e9vcmthITygLrPfojnB4nxsskw21O/iE3GRG82+n/aPgzT++TW8fY5765PjEAvRHLk1fa0Atw8uCVzrgniE9AOCxwJt0eNbZxX3GlCwKSXlDBVIj2qWMSpoWCuQ0SZF4WJKQu7IYz8DzVzPNGg5hqbWWqtzXBixNz9qkiODzShUClkETwDocbjtBJp9Wh5QW8T8PXrgq9nCDI3qaA==";
let decoded = decompress_encoded_url(url).unwrap();
let net = PetriNet::from_json(decoded.clone()).unwrap();
let decoded = decompress_encoded_url(url).expect("Decompression failed");
let net = PetriNet::from_json_str(decoded).expect("Failed to parse the given JSON.");
assert_eq!(net.places.len(), 4);
assert_eq!(net.transitions.len(), 1);
}
}
}
62 changes: 45 additions & 17 deletions src/dsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use crate::vasm::StateMachine;
/// # Example
///
/// ```
/// use pflow_metamodel::dsl::FlowDsl;
/// use pflow_metamodel::dsl::Dsl;
/// use pflow_metamodel::vasm::Vasm;
/// fn model_test_code(p: &mut dyn FlowDsl) {
/// fn model_test_code(p: &mut dyn Dsl) {
/// p.model_type("petriNet");
///
/// let r = "default";
Expand All @@ -31,7 +31,7 @@ use crate::vasm::StateMachine;
///
/// let model = <dyn Vasm>::new(model_test_code);
/// ```
pub trait FlowDsl {
pub trait Dsl {
/// Sets the model type of the Petri net.
fn model_type(&mut self, model_type: &str);
/// Adds a cell (place) to the Petri net.
Expand Down Expand Up @@ -89,7 +89,7 @@ impl<'a> Builder<'a> {
}
}

impl<'a> FlowDsl for Builder<'a> {
impl Dsl for Builder<'_> {
fn model_type(&mut self, model_type: &str) {
self.net.model_type = model_type.to_string();
}
Expand All @@ -102,27 +102,54 @@ impl<'a> FlowDsl for Builder<'a> {
x: i32,
y: i32,
) -> &'b str {
let offset = self.net.places.len() as i32;
let offset: i32 = self.net.places.len().try_into().expect("too many places");
self.net.add_place(label, offset, initial, capacity, x, y);
return label;
label
}

fn func<'b>(&mut self, label: &'b str, role: &str, x: i32, y: i32) -> &'b str {
self.net.add_transition(label, role, x, y);
return label;
label
}

fn arrow(&mut self, source: &str, target: &str, weight: i32) {
assert!(weight > 0, "weight must be positive");
self.net.add_arc(source, target, Some(weight), None, None, None, None);
self.net
.add_arc(ArcParams {
source,
target,
weight: Some(weight),
consume: None,
produce: None,
inhibit: None,
read: None,
});
}

fn guard(&mut self, source: &str, target: &str, weight: i32) {
assert!(weight > 0, "weight must be positive");
self.net.add_arc(source, target, Some(weight), Some(true), None, Some(true), None);
self.net.add_arc(ArcParams {
source,
target,
weight: Some(weight),
consume: Some(true),
produce: None,
inhibit: Some(true),
read: None,
});
}
}

pub struct ArcParams<'a> {
pub source: &'a str,
pub target: &'a str,
pub weight: Option<i32>,
pub consume: Option<bool>,
pub produce: Option<bool>,
pub inhibit: Option<bool>,
pub read: Option<bool>,
}

#[cfg(test)]
mod tests {
use crate::model::Model;
Expand All @@ -137,16 +164,17 @@ mod tests {

impl TestModel {
fn to_link(&self) -> String {
format!("{}{}", "https://pflow.dev/p/?z=", self.model.net.to_zblob().base64_zipped.replace(" ", "+"))
format!(
"{}{}",
"http://example.com/?z=",
self.model.net.to_zblob().base64_zipped.replace(' ', "+")
)
}

fn new() -> Self {
let model = Model::new(model_test_code);
let state = model.vm.initial_vector().clone();
Self {
model,
state,
}
let state = model.vm.initial_vector();
Self { model, state }
}

fn assert_underflow(&self, action: &str) -> Transaction {
Expand Down Expand Up @@ -176,12 +204,12 @@ mod tests {
fn assert_pass(&mut self, action: &str) -> Transaction {
let res = self.model.vm.transform(&self.state, action, 1);
assert!(res.is_ok(), "expected pass");
self.state = res.output.clone();
self.state.clone_from(&res.output);
res
}
}

fn model_test_code(p: &mut dyn FlowDsl) {
fn model_test_code(p: &mut dyn Dsl) {
p.model_type("petriNet");

let r = "default";
Expand Down
2 changes: 1 addition & 1 deletion src/fixtures.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// This is a petri net model that represents the dining philosophers problem using chopsticks and philosophers.
pub const DINING_PHILOSOPHERS: &'static str = r#"
pub const DINING_PHILOSOPHERS: &str = r#"
{
"modelType": "petriNet",
"version": "v0",
Expand Down
432 changes: 431 additions & 1 deletion src/lib.rs

Large diffs are not rendered by default.

38 changes: 4 additions & 34 deletions src/model.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::dsl::FlowDsl;
use crate::dsl::Dsl;
use crate::petri_net::PetriNet;
use crate::vasm::StateMachine;

pub struct Model {
pub net: PetriNet,
pub declaration: Vec<fn(&mut dyn FlowDsl)>,
pub declaration: Vec<fn(&mut dyn Dsl)>,
pub vm: Box<StateMachine>,
}

impl Model {
pub fn new(func: fn(&mut dyn FlowDsl)) -> Self {
pub fn new(func: fn(&mut dyn Dsl)) -> Self {
let mut net = PetriNet::new();
let vm = Box::new(net.declare(func).as_vasm());
Self {
Expand All @@ -19,39 +19,9 @@ impl Model {
}
}

pub fn declare(&mut self, func: fn(&mut dyn FlowDsl)) -> &mut Model {
pub fn declare(&mut self, func: fn(&mut dyn Dsl)) -> &mut Model {
self.declaration.push(func);
self.vm = Box::new(self.net.declare(func).as_vasm());
self
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_model() {
let mut model = Model::new(|p| {
p.model_type("petriNet");
p.cell("b", Option::from(1), None, 0, 0);
p.func("f", "default", 1, 1);
});

model.declare(|p| {
p.cell("a", Option::from(1), None, 0, 0);
p.func("g", "default", 1, 1);
p.arrow("a", "f", 1);
});

assert_eq!(model.net.model_type, "petriNet");
let zblob = model.net.to_zblob();
assert_eq!(
zblob.ipfs_cid,
"zb2rhXz6Zi73pN9tyWzNGCLUCd9MLvAkupcBKXpCvrV87Rch4"
);

let r = model.vm.roles.get("default").unwrap();
assert!(r, "expected role");
}
}
Loading

0 comments on commit eea128a

Please sign in to comment.