1
#![forbid(unsafe_code)]
2

            
3
use std::ffi::OsStr;
4
use std::fs::File;
5
use std::path::Path;
6

            
7
use merc_utilities::MercError;
8
use merc_utilities::Timing;
9

            
10
use crate::LTS;
11
use crate::LabelledTransitionSystem;
12
use crate::LtsMultiAction;
13
use crate::read_aut;
14
use crate::read_bcg;
15
use crate::read_lts;
16

            
17
/// Convenience macro to call `GenericLts::apply` with the same function for both variants.
18
/// Useful with generic functions that can be monomorphized for both label types.
19
///
20
/// Examples:
21
/// - apply_lts!(lts, my_fn)
22
/// - apply_lts!(lts, |lts| do_something(lts))
23
#[macro_export]
24
macro_rules! apply_lts {
25
    ($lts:expr, $arguments:expr, $f:path) => {
26
        $lts.apply($arguments, $f, $f)
27
    };
28
    ($lts:expr, $arguments:expr, $f:expr) => {
29
        $lts.apply($arguments, $f, $f)
30
    };
31
}
32

            
33
/// Convenience macro to apply a function to a pair of `GenericLts` only when both
34
/// are the same variant; returns an error otherwise.
35
///
36
/// Examples:
37
/// - apply_lts_pair!(lhs, rhs, args, my_fn)
38
/// - apply_lts_pair!(lhs, rhs, args, |a, b, args| do_something(a, b, args))
39
#[macro_export]
40
macro_rules! apply_lts_pair {
41
    ($lhs:expr, $rhs:expr, $arguments:expr, $f:path) => {
42
        $lhs.apply_pair($rhs, $arguments, $f, $f)
43
    };
44
    ($lhs:expr, $rhs:expr, $arguments:expr, $f:expr) => {
45
        $lhs.apply_pair($rhs, $arguments, $f, $f)
46
    };
47
}
48

            
49
/// Explicitly specify the LTS file format.
50
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
51
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
52
pub enum LtsFormat {
53
    /// The AUTomaton or ALDEBARAN format
54
    Aut,
55
    /// The mCRL2 binary LTS format
56
    Lts,
57
    /// The CADP BCG format (requires 'cadp' feature)
58
    Bcg,
59
}
60

            
61
/// Guesses the LTS file format from the file extension.
62
pub fn guess_lts_format_from_extension(path: &Path, format: Option<LtsFormat>) -> Option<LtsFormat> {
63
    if let Some(format) = format {
64
        return Some(format);
65
    }
66

            
67
    if path.extension() == Some(OsStr::new("aut")) {
68
        Some(LtsFormat::Aut)
69
    } else if path.extension() == Some(OsStr::new("lts")) {
70
        Some(LtsFormat::Lts)
71
    } else if path.extension() == Some(OsStr::new("bcg")) {
72
        Some(LtsFormat::Bcg)
73
    } else {
74
        None
75
    }
76
}
77

            
78
/// A general struct to deal with the polymorphic LTS types. The `apply_lts`
79
/// macro can be then used to conveniently apply functions which are generic on
80
/// the LTS trait to all variants.
81
pub enum GenericLts {
82
    /// The LTS in the Aldebaran format.
83
    Aut(LabelledTransitionSystem<String>),
84
    /// The LTS in the mCRL2 .lts format.
85
    Lts(LabelledTransitionSystem<LtsMultiAction>),
86
    /// The LTS in the CADP BCG format.
87
    Bcg(LabelledTransitionSystem<String>),
88
}
89

            
90
impl GenericLts {
91
    /// Applies the given function to both LTSs when they are the same variant.
92
    /// Returns an error if the variants do not match.
93
    pub fn apply_pair<T, FAut, FLts, R>(self, other: GenericLts, arguments: T, apply_aut: FAut, apply_lts: FLts) -> R
94
    where
95
        FAut: FnOnce(LabelledTransitionSystem<String>, LabelledTransitionSystem<String>, T) -> R,
96
        FLts: FnOnce(LabelledTransitionSystem<LtsMultiAction>, LabelledTransitionSystem<LtsMultiAction>, T) -> R,
97
    {
98
        match (self, other) {
99
            (GenericLts::Aut(a), GenericLts::Aut(b)) => apply_aut(a, b, arguments),
100
            (GenericLts::Lts(a), GenericLts::Lts(b)) => apply_lts(a, b, arguments),
101
            (GenericLts::Bcg(a), GenericLts::Bcg(b)) => apply_aut(a, b, arguments),
102
            _ => unreachable!("Mismatched GenericLts variants in apply_pair; this indicates a programming error"),
103
        }
104
    }
105
}
106

            
107
impl GenericLts {
108
    pub fn apply<T, F, G, R>(self, arguments: T, apply_aut: F, apply_lts: G) -> R
109
    where
110
        F: FnOnce(LabelledTransitionSystem<String>, T) -> R,
111
        G: FnOnce(LabelledTransitionSystem<LtsMultiAction>, T) -> R,
112
    {
113
        match self {
114
            GenericLts::Aut(lts) => apply_aut(lts, arguments),
115
            GenericLts::Lts(lts) => apply_lts(lts, arguments),
116
            GenericLts::Bcg(lts) => apply_aut(lts, arguments),
117
        }
118
    }
119

            
120
    // These are convenience functions to get LTS metrics.
121

            
122
    /// Returns the number of states in the LTS.
123
    pub fn num_of_states(&self) -> usize {
124
        match self {
125
            GenericLts::Aut(lts) => lts.num_of_states(),
126
            GenericLts::Lts(lts) => lts.num_of_states(),
127
            GenericLts::Bcg(lts) => lts.num_of_states(),
128
        }
129
    }
130

            
131
    /// Returns the number of transitions in the LTS.
132
    pub fn num_of_transitions(&self) -> usize {
133
        match self {
134
            GenericLts::Aut(lts) => lts.num_of_transitions(),
135
            GenericLts::Lts(lts) => lts.num_of_transitions(),
136
            GenericLts::Bcg(lts) => lts.num_of_transitions(),
137
        }
138
    }
139
}
140

            
141
/// Reads an explicit labelled transition system from the given path and format.
142
pub fn read_explicit_lts(
143
    path: &Path,
144
    format: LtsFormat,
145
    hidden_labels: Vec<String>,
146
    timing: &mut Timing,
147
) -> Result<GenericLts, MercError> {
148
    timing.measure("read_explicit_lts", || {
149
        let result = match format {
150
            LtsFormat::Aut => {
151
                let file = File::open(path)?;
152
                GenericLts::Aut(read_aut(&file, hidden_labels)?)
153
            }
154
            LtsFormat::Lts => {
155
                let file = File::open(path)?;
156
                GenericLts::Lts(read_lts(&file, hidden_labels, false)?)
157
            }
158
            LtsFormat::Bcg => GenericLts::Bcg(read_bcg(path, hidden_labels)?),
159
        };
160

            
161
        Ok(result)
162
    })
163
}