1
use std::ffi::OsStr;
2
use std::path::Path;
3

            
4
use clap::ValueEnum;
5
use merc_utilities::MercError;
6
use merc_utilities::Timing;
7

            
8
use crate::LTS;
9
use crate::LabelledTransitionSystem;
10
use crate::MultiAction;
11
use crate::read_aut;
12
use crate::read_lts;
13

            
14
/// Explicitly specify the LTS file format.
15
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq)]
16
pub enum LtsFormat {
17
    Aut,
18
    Lts,
19
}
20

            
21
/// Guesses the LTS file format from the file extension.
22
pub fn guess_lts_format_from_extension(path: &Path, format: Option<LtsFormat>) -> Option<LtsFormat> {
23
    if let Some(format) = format {
24
        return Some(format);
25
    }
26

            
27
    if path.extension() == Some(OsStr::new("aut")) {
28
        Some(LtsFormat::Aut)
29
    } else if path.extension() == Some(OsStr::new("lts")) {
30
        Some(LtsFormat::Lts)
31
    } else {
32
        None
33
    }
34
}
35

            
36
/// A general struct to deal with the polymorphic LTS types. The `apply_lts`
37
/// macro can be then used to conveniently apply functions which are generic on
38
/// the LTS trait to all variants.
39
pub enum GenericLts {
40
    /// The LTS in the Aldebaran format.
41
    Aut(LabelledTransitionSystem<String>),
42
    /// The LTS in the mCRL2 .lts format.
43
    Lts(LabelledTransitionSystem<MultiAction>),
44
}
45

            
46
/// Convenience macro to call `GenericLts::apply` with the same function for both variants.
47
/// Useful with generic functions that can be monomorphized for both label types.
48
///
49
/// Examples:
50
/// - apply_lts!(lts, my_fn)
51
/// - apply_lts!(lts, |lts| do_something(lts))
52
#[macro_export]
53
macro_rules! apply_lts {
54
    ($lts:expr, $arguments:expr, $f:path) => {
55
        $lts.apply($arguments, $f, $f)
56
    };
57
    ($lts:expr, $arguments:expr, $f:expr) => {
58
        $lts.apply($arguments, $f, $f)
59
    };
60
}
61

            
62
/// Convenience macro to apply a function to a pair of `GenericLts` only when both
63
/// are the same variant; returns an error otherwise.
64
///
65
/// Examples:
66
/// - apply_lts_pair!(lhs, rhs, args, my_fn)
67
/// - apply_lts_pair!(lhs, rhs, args, |a, b, args| do_something(a, b, args))
68
#[macro_export]
69
macro_rules! apply_lts_pair {
70
    ($lhs:expr, $rhs:expr, $arguments:expr, $f:path) => {
71
        $lhs.apply_pair($rhs, $arguments, $f, $f)
72
    };
73
    ($lhs:expr, $rhs:expr, $arguments:expr, $f:expr) => {
74
        $lhs.apply_pair($rhs, $arguments, $f, $f)
75
    };
76
}
77

            
78
impl GenericLts {
79
    /// Applies the given function to both LTSs when they are the same variant.
80
    /// Returns an error if the variants do not match.
81
    pub fn apply_pair<T, FAut, FLts, R>(self, other: GenericLts, arguments: T, apply_aut: FAut, apply_lts: FLts) -> R
82
    where
83
        FAut: FnOnce(LabelledTransitionSystem<String>, LabelledTransitionSystem<String>, T) -> R,
84
        FLts: FnOnce(LabelledTransitionSystem<MultiAction>, LabelledTransitionSystem<MultiAction>, T) -> R,
85
    {
86
        match (self, other) {
87
            (GenericLts::Aut(a), GenericLts::Aut(b)) => apply_aut(a, b, arguments),
88
            (GenericLts::Lts(a), GenericLts::Lts(b)) => apply_lts(a, b, arguments),
89
            _ => unreachable!("Mismatched LTS variants"),
90
        }
91
    }
92
}
93

            
94
impl GenericLts {
95
    pub fn apply<T, F, G, R>(self, arguments: T, apply_aut: F, apply_lts: G) -> R
96
    where
97
        F: FnOnce(LabelledTransitionSystem<String>, T) -> R,
98
        G: FnOnce(LabelledTransitionSystem<MultiAction>, T) -> R,
99
    {
100
        match self {
101
            GenericLts::Aut(lts) => apply_aut(lts, arguments),
102
            GenericLts::Lts(lts) => apply_lts(lts, arguments),
103
        }
104
    }
105

            
106
    // These are convenience functions to get LTS metrics.
107

            
108
    /// Returns the number of states in the LTS.
109
    pub fn num_of_states(&self) -> usize {
110
        match self {
111
            GenericLts::Aut(lts) => lts.num_of_states(),
112
            GenericLts::Lts(lts) => lts.num_of_states(),
113
        }
114
    }
115

            
116
    /// Returns the number of transitions in the LTS.
117
    pub fn num_of_transitions(&self) -> usize {
118
        match self {
119
            GenericLts::Aut(lts) => lts.num_of_transitions(),
120
            GenericLts::Lts(lts) => lts.num_of_transitions(),
121
        }
122
    }
123
}
124

            
125
/// Reads an explicit labelled transition system from the given path and format.
126
pub fn read_explicit_lts(
127
    path: &Path,
128
    format: LtsFormat,
129
    hidden_labels: Vec<String>,
130
    timing: &mut Timing,
131
) -> Result<GenericLts, MercError> {
132
    let file = std::fs::File::open(path)?;
133
    let mut time_read = timing.start("read_aut");
134

            
135
    let result = match format {
136
        LtsFormat::Aut => GenericLts::Aut(read_aut(&file, hidden_labels)?),
137
        LtsFormat::Lts => GenericLts::Lts(read_lts(&file, hidden_labels)?),
138
    };
139

            
140
    time_read.finish();
141
    Ok(result)
142
}