ptnet_core/
fmt.rs

1/*!
2This module provides traits for producing formatted representations of nets.
3
4- [`NetFormatter`] : Format the structure of a net.
5- [`MarkedNetFormatter`] : Format a marking of a net, with or without a list of enabled transitions.
6
7An implementation of the [`NetFormatter`], [`NetMatrixFormatter`], is provided that is independent of
8the specific net implementation as it generates only a simple table of net connectivity.
9
10Additionally, the following helper functions take a net, or net and marking, with a formatter.
11
12- [`net_to_file`] - write the formatted representation to the named file;
13  and [`marked_net_to_file`].
14- [`net_to_string`] - produce a formatted string representation of the net;
15  and [`marked_net_to_string`].
16- [`print`] - print the formatted representation to stdout;
17  and [`print_marked_net`].
18
19*/
20
21use crate::error::Error;
22use crate::net::{Arc, Net, Place, Transition};
23use crate::sim::{Marking, Tokens};
24use crate::NodeId;
25use std::fs::OpenOptions;
26use std::io::Write;
27use std::marker::PhantomData;
28use std::path::Path;
29
30// ------------------------------------------------------------------------------------------------
31// Public Types
32// ------------------------------------------------------------------------------------------------
33
34///
35/// Implementations of this trait will write a representation of the given `net`.
36///
37pub trait NetFormatter {
38    type Place: Place;
39    type Transition: Transition;
40    type Arc: Arc;
41    type Net: Net<Place = Self::Place, Transition = Self::Transition, Arc = Self::Arc>;
42
43    ///
44    /// Format the given `net`.
45    ///
46    fn fmt_net<W: Write>(&self, w: &mut W, net: &Self::Net) -> Result<(), Error>;
47}
48
49///
50/// Implementations of this trait will write a representation of the given marked `net`.
51///
52pub trait MarkedNetFormatter {
53    type Place: Place;
54    type Transition: Transition;
55    type Arc: Arc;
56    type Net: Net<Place = Self::Place, Transition = Self::Transition, Arc = Self::Arc>;
57    type Tokens: Tokens;
58    type Marking: Marking<Tokens = Self::Tokens>;
59
60    ///
61    /// Format the given `net`, marked with `marking` and optionally a list of currently `enabled`
62    /// transitions.
63    ///
64    fn fmt_marked_net<W: Write>(
65        &self,
66        w: &mut W,
67        net: &Self::Net,
68        marking: &Self::Marking,
69        enabled: Option<&[NodeId]>,
70    ) -> Result<(), Error>;
71}
72
73///
74/// Write a matrix with all nodes showing the arcs that connect them. This outputs the table in the
75/// markdown/org-mode format.
76///
77/// ## Example
78///
79/// Given the simple net \\(\circ\rightarrow\rule[-1pt]{3pt}{0.75em}\rightarrow\circ\\), the matrix
80/// is as follows:
81///
82/// |      |   p0 |   p1 |   t0 |
83/// |------|------|------|------|
84/// | p0   |      |      |   ↗︎  |
85/// | p1   |      |      |      |
86/// | t0   |      |   ↗︎  |      |
87///
88#[derive(Debug)]
89pub struct NetMatrixFormatter<P, T, A, N>
90where
91    P: Place,
92    T: Transition,
93    A: Arc,
94    N: Net<Place = P, Transition = T, Arc = A>,
95{
96    nothing: PhantomData<N>,
97}
98
99// ------------------------------------------------------------------------------------------------
100// Public Functions
101// ------------------------------------------------------------------------------------------------
102
103///
104/// Helper function that writes a representation of the given `net` to a file. If `append` is
105/// `true` the representation is appended to the file, else the file is truncated.
106///
107pub fn net_to_file<F: NetFormatter, P: AsRef<Path>>(
108    net: &F::Net,
109    path: P,
110    append: bool,
111    formatter: &mut F,
112) -> Result<(), Error> {
113    let mut file = OpenOptions::new()
114        .write(true)
115        .create(true)
116        .append(append)
117        .open(path)?;
118    formatter.fmt_net(&mut file, net)
119}
120
121///
122/// Helper function that will return a representation of the given `net` as a string value.
123///
124pub fn net_to_string<F: NetFormatter>(net: &F::Net, formatter: &mut F) -> Result<String, Error> {
125    let mut buffer = Vec::new();
126    formatter.fmt_net(&mut buffer, net)?;
127    let string = String::from_utf8(buffer)?;
128    Ok(string)
129}
130
131///
132/// Helper function that  writes a representation of the given `net` to `std::io::stdout`.
133///
134pub fn print_net<F: NetFormatter>(net: &F::Net, formatter: &mut F) -> Result<(), Error> {
135    let stdout = std::io::stdout();
136    let mut handle = stdout.lock();
137    formatter.fmt_net(&mut handle, net)
138}
139
140// ------------------------------------------------------------------------------------------------
141
142///
143/// Helper function that writes a representation of the given `net` and `marking` to a file. If
144///  `append` is `true` the representation is appended to the file, else the file is truncated.
145///
146pub fn marked_net_to_file<F: MarkedNetFormatter, P: AsRef<Path>>(
147    net: &F::Net,
148    marking: &F::Marking,
149    enabled: Option<&[NodeId]>,
150    path: P,
151    append: bool,
152    formatter: &mut F,
153) -> Result<(), Error> {
154    let mut file = OpenOptions::new()
155        .write(true)
156        .create(true)
157        .append(append)
158        .open(path)?;
159    formatter.fmt_marked_net(&mut file, net, marking, enabled)
160}
161
162///
163/// Helper function that will return a representation of the given `net` and `marking` as a
164/// string value.
165///
166pub fn marked_net_to_string<F: MarkedNetFormatter>(
167    net: &F::Net,
168    marking: &F::Marking,
169    enabled: Option<&[NodeId]>,
170    formatter: &mut F,
171) -> Result<String, Error> {
172    let mut buffer = Vec::new();
173    formatter.fmt_marked_net(&mut buffer, net, marking, enabled)?;
174    let string = String::from_utf8(buffer)?;
175    Ok(string)
176}
177
178///
179/// Helper function that  writes a representation of the given `net` and `marking` to
180/// `std::io::stdout`.
181///
182pub fn print_marked_net<F: MarkedNetFormatter>(
183    net: &F::Net,
184    marking: &F::Marking,
185    enabled: Option<&[NodeId]>,
186    formatter: &mut F,
187) -> Result<(), Error> {
188    let stdout = std::io::stdout();
189    let mut handle = stdout.lock();
190    formatter.fmt_marked_net(&mut handle, net, marking, enabled)
191}
192
193// ------------------------------------------------------------------------------------------------
194// Implementations
195// ------------------------------------------------------------------------------------------------
196
197const FORMAT_FIELD_WIDTH: usize = 6;
198const MATRIX_ARROW: &str = "-^";
199
200impl<P, T, A, N> Default for NetMatrixFormatter<P, T, A, N>
201where
202    P: Place,
203    T: Transition,
204    A: Arc,
205    N: Net<Place = P, Transition = T, Arc = A>,
206{
207    fn default() -> Self {
208        Self {
209            nothing: Default::default(),
210        }
211    }
212}
213
214impl<P, T, A, N> NetFormatter for NetMatrixFormatter<P, T, A, N>
215where
216    P: Place,
217    T: Transition,
218    A: Arc,
219    N: Net<Place = P, Transition = T, Arc = A>,
220{
221    type Place = P;
222    type Transition = T;
223    type Arc = A;
224    type Net = N;
225
226    fn fmt_net<W: Write>(&self, w: &mut W, net: &Self::Net) -> Result<(), Error> {
227        let mut places: Vec<NodeId> = net.places().iter().map(|place| place.id()).collect();
228        places.sort();
229        let mut transitions: Vec<NodeId> = net
230            .transitions()
231            .iter()
232            .map(|transition| transition.id())
233            .collect();
234        transitions.sort();
235
236        writeln!(
237            w,
238            "| {:>FORMAT_FIELD_WIDTH$} | {} |",
239            "",
240            places
241                .iter()
242                .map(|id| format!("{:>FORMAT_FIELD_WIDTH$}", id.as_place_string()))
243                .chain(
244                    transitions
245                        .iter()
246                        .map(|id| format!("{:>FORMAT_FIELD_WIDTH$}", id.as_transition_string()))
247                )
248                .collect::<Vec<String>>()
249                .join(" | ")
250        )?;
251
252        let fields = places.len() + transitions.len();
253        let width = FORMAT_FIELD_WIDTH + 2; // for pad spaces.
254        writeln!(
255            w,
256            "|{}|",
257            (0..=fields)
258                .map(|_| format!("{:-<width$}", ""))
259                .collect::<Vec<String>>()
260                .join("+")
261        )?;
262
263        let arcs: Vec<(NodeId, NodeId)> = net
264            .arcs()
265            .iter()
266            .map(|arc| (arc.source(), arc.target()))
267            .collect();
268
269        for place in &places {
270            writeln!(
271                w,
272                "| {:>FORMAT_FIELD_WIDTH$} | {} |",
273                place.as_place_string(),
274                places
275                    .iter()
276                    .map(|_| format!("{:>FORMAT_FIELD_WIDTH$}", ""))
277                    .chain(transitions.iter().map(|id| format!(
278                        "{:>FORMAT_FIELD_WIDTH$}",
279                        if arcs.contains(&(*place, *id)) {
280                            MATRIX_ARROW
281                        } else {
282                            ""
283                        }
284                    )))
285                    .collect::<Vec<String>>()
286                    .join(" | ")
287            )?;
288        }
289
290        for transition in &transitions {
291            writeln!(
292                w,
293                "| {:>FORMAT_FIELD_WIDTH$} | {} |",
294                transition.as_transition_string(),
295                places
296                    .iter()
297                    .map(|id| format!(
298                        "{:>FORMAT_FIELD_WIDTH$}",
299                        if arcs.contains(&(*id, *transition)) {
300                            MATRIX_ARROW
301                        } else {
302                            ""
303                        }
304                    ))
305                    .chain(
306                        transitions
307                            .iter()
308                            .map(|_| format!("{:>FORMAT_FIELD_WIDTH$}", ""))
309                    )
310                    .collect::<Vec<String>>()
311                    .join(" | ")
312            )?;
313        }
314
315        Ok(())
316    }
317}