pns 0.18.3

A simple and efficient library wrapper for simulating a minimal form of petri nets
Documentation
use std::{
    fs::File,
    io::{Error, ErrorKind, Write},
    marker::PhantomData,
    mem::MaybeUninit,
    path::Path,
};

use crate::{
    core::{
        ids::{Ids, Pid, Place, Tid, Transition},
        utils::read_values,
    },
    sys::{
        functions::net::*,
        types::{Net, Node},
    },
};

impl Net {
    /// Create a new, empty petri net.
    pub fn new() -> Self {
        unsafe {
            let mut net = MaybeUninit::uninit();
            pnsCreateNet(net.as_mut_ptr());
            net.assume_init()
        }
    }

    /// Load a petri net from a file.
    pub fn load(filename: &Path) -> Result<Self, Error> {
        let values = read_values(filename)?;

        unsafe {
            let mut net = MaybeUninit::uninit();

            if pnsLoadNet(net.as_mut_ptr(), values.len(), &raw const values[0]) {
                Ok(net.assume_init())
            } else {
                Err(Error::new(
                    ErrorKind::InvalidData,
                    "File does not represent a petri net",
                ))
            }
        }
    }

    /// Save the petri net to a file. Result represents success.
    pub fn save(&self, filename: &Path) -> Result<(), Error> {
        let mut file = File::create(filename)?;

        let data = unsafe {
            let count = pnsNet_serializeSize(self);
            let mut data = vec![0u32; count];
            pnsNet_serialize(self, &raw mut data[0]);
            data
        };

        for value in data {
            file.write_all(&value.to_le_bytes())?;
        }

        Ok(())
    }

    /// The count of transitions of the petri net.
    pub fn transition_count(&self) -> usize {
        self.transitions.count
    }

    /// An iterator over the ids of existing transitions.
    pub fn transition_ids(&self) -> Ids<Transition> {
        Ids {
            start: 0,
            end: self.transitions.count,
            kind: PhantomData::<Transition>,
        }
    }

    /// A node representing a transition of the petri net.
    pub fn transition(&self, tid: Tid) -> &Node<Pid> {
        unsafe {
            &std::slice::from_raw_parts(self.transitions.nodes.cast(), self.transitions.count)
                [tid.index]
        }
    }

    /// Returns the index of the next reusable transition.
    pub fn reusable_transition(&self) -> Option<Tid> {
        unsafe { self.transitions.reusable.as_ref() }.map(|list| Tid::new(list.index))
    }

    /// The count of places of the petri net.
    pub fn place_count(&self) -> usize {
        self.places.count
    }

    /// An iterator over the ids of existing places.
    pub fn place_ids(&self) -> Ids<Place> {
        Ids {
            start: 0,
            end: self.places.count,
            kind: PhantomData::<Place>,
        }
    }

    /// A node representing a place of the petri net.
    pub fn place(&self, pid: Pid) -> &Node<Tid> {
        unsafe {
            &std::slice::from_raw_parts(self.places.nodes.cast(), self.places.count)[pid.index]
        }
    }

    /// Returns the index of the next reusable place.
    pub fn reusable_place(&self) -> Option<Pid> {
        unsafe { self.places.reusable.as_ref() }.map(|list| Pid::new(list.index))
    }

    /// The initial token count for a place of the petri net.
    pub fn initial_token_count(&self, pid: Pid) -> usize {
        unsafe {
            std::slice::from_raw_parts(self.initial_token_counts, self.places.count)[pid.index]
        }
    }

    /// Add a new place to the petri net and get the index.
    pub fn add_place(&mut self) -> Pid {
        unsafe { Pid::new(pnsNet_addPlace(self)) }
    }

    /// Add a new transition to the petri net and get the index.
    pub fn add_transition(&mut self) -> Tid {
        unsafe { Tid::new(pnsNet_addTransition(self)) }
    }

    /// Remove a place at index `pid` from petri net.
    pub fn remove_place(&mut self, pid: Pid) {
        pid.assert(self.places.count);
        unsafe { pnsNet_removePlace(self, pid.index) }
    }

    /// Remove a transition at index `tid` from petri net.
    pub fn remove_transition(&mut self, tid: Tid) {
        tid.assert(self.transitions.count);
        unsafe { pnsNet_removeTransition(self, tid.index) }
    }

    /// Make a connection in to the transition with index `tid` from place with index `pid`.
    /// Result represents success.
    pub fn connect_place_to_transition(&mut self, pid: Pid, tid: Tid) -> bool {
        tid.assert(self.transitions.count);
        pid.assert(self.places.count);
        unsafe { pnsNet_connectPlaceToTransition(self, pid.index, tid.index) }
    }

    /// Make a connection out from the transition with index `tid` to place with index `pid`.
    /// Result represents success.
    pub fn connect_transition_to_place(&mut self, tid: Tid, pid: Pid) -> bool {
        tid.assert(self.transitions.count);
        pid.assert(self.places.count);
        unsafe { pnsNet_connectTransitionToPlace(self, tid.index, pid.index) }
    }

    /// Remove the connection in to transition with index `tid` from place with index `pid`.
    pub fn disconnect_place_to_transition(&mut self, pid: Pid, tid: Tid) {
        tid.assert(self.transitions.count);
        pid.assert(self.places.count);
        unsafe { pnsNet_disconnectPlaceToTransition(self, pid.index, tid.index) }
    }

    /// Remove the connection out from transition with index `tid` to place with index `pid`.
    pub fn disconnect_transition_to_place(&mut self, tid: Tid, pid: Pid) {
        tid.assert(self.transitions.count);
        pid.assert(self.places.count);
        unsafe { pnsNet_disconnectTransitionToPlace(self, tid.index, pid.index) }
    }

    /// Increase the initial token count in place indexed by `pid`.
    pub fn add_initial_tokens(&mut self, pid: Pid, count: usize) -> usize {
        pid.assert(self.places.count);
        unsafe { pnsNet_addInitialTokens(self, pid.index, count) }
    }

    /// Add a new place to the petri net, connct it to the specified transitions and get the index.
    pub fn add_connected_place(&mut self, in_tids: &[Tid], out_tids: &[Tid]) -> Pid {
        for tid in in_tids.iter().chain(out_tids) {
            tid.assert(self.transitions.count);
        }
        Pid::new(unsafe {
            pnsNet_addConnectedPlace(
                self,
                in_tids.len(),
                in_tids.as_ptr().cast(),
                out_tids.len(),
                out_tids.as_ptr().cast(),
            )
        })
    }

    /// Add a new transition to the petri net, connct it to the specified places and get the index.
    pub fn add_connected_transition(&mut self, in_pids: &[Pid], out_pids: &[Pid]) -> Tid {
        for pid in in_pids.iter().chain(out_pids) {
            pid.assert(self.places.count);
        }
        Tid::new(unsafe {
            pnsNet_addConnectedTransition(
                self,
                in_pids.len(),
                in_pids.as_ptr().cast(),
                out_pids.len(),
                out_pids.as_ptr().cast(),
            )
        })
    }

    /// Duplicate the place and get the index of the clone.
    pub fn duplicate_place(&mut self, pid: Pid) -> Pid {
        pid.assert(self.places.count);
        Pid::new(unsafe { pnsNet_duplicatePlace(self, pid.index) })
    }

    /// Duplicate the transition and get the index of the clone.
    pub fn duplicate_transition(&mut self, tid: Tid) -> Tid {
        tid.assert(self.transitions.count);
        Tid::new(unsafe { pnsNet_duplicateTransition(self, tid.index) })
    }
}

impl Clone for Net {
    fn clone(&self) -> Self {
        unsafe {
            let mut net = MaybeUninit::uninit();
            pnsCloneNet(net.as_mut_ptr(), self);
            net.assume_init()
        }
    }
}

impl Drop for Net {
    fn drop(&mut self) {
        unsafe {
            pnsDestroyNet(self);
        }
    }
}

impl Default for Net {
    fn default() -> Self {
        Self::new()
    }
}