Skip to main content

encounter_argumentation/
arg_id.rs

1//! `ArgumentId`: the identifier type for argument nodes in the
2//! encounter-level weighted bipolar framework.
3//!
4//! An `ArgumentId` is the stringified rendering of a literal
5//! (e.g. `"fortify_east"` for a positive atom, `"¬deny_claim"` for a
6//! negated literal). Two scheme instances that share a conclusion
7//! literal share the same `ArgumentId`, so both count as supporting
8//! the same argument node. This is the correct convergence behaviour.
9
10use argumentation::aspic::Literal;
11
12/// Opaque identifier for an argument node in the weighted bipolar
13/// framework. Constructed from a `Literal` via `From`.
14#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct ArgumentId(String);
16
17impl ArgumentId {
18    /// Construct an `ArgumentId` from a raw string. Prefer `From<&Literal>`
19    /// when converting from scheme conclusions.
20    #[must_use]
21    pub fn new(s: impl Into<String>) -> Self {
22        Self(s.into())
23    }
24
25    /// The underlying string, e.g. for AIF export or logging.
26    #[must_use]
27    pub fn as_str(&self) -> &str {
28        &self.0
29    }
30}
31
32impl From<&Literal> for ArgumentId {
33    /// **Ambiguity warning.** `Literal::Atom("¬foo")` and
34    /// `Literal::Neg("foo")` both render to `"¬foo"` and will
35    /// therefore collide on the same `ArgumentId`. Phase A accepts
36    /// this because the default scheme catalog never mints atoms whose
37    /// names begin with `¬`; consumers minting literals dynamically
38    /// should avoid leading `¬` in atom names.
39    fn from(lit: &Literal) -> Self {
40        Self(lit.to_string())
41    }
42}
43
44impl From<Literal> for ArgumentId {
45    fn from(lit: Literal) -> Self {
46        Self(lit.to_string())
47    }
48}
49
50impl std::fmt::Display for ArgumentId {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        f.write_str(&self.0)
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn new_wraps_string() {
62        let id = ArgumentId::new("fortify_east");
63        assert_eq!(id.as_str(), "fortify_east");
64    }
65
66    #[test]
67    fn from_positive_literal_renders_plain() {
68        let lit = Literal::atom("fortify_east");
69        let id: ArgumentId = (&lit).into();
70        assert_eq!(id.as_str(), "fortify_east");
71    }
72
73    #[test]
74    fn from_negated_literal_renders_with_prefix() {
75        let lit = Literal::neg("deny_claim");
76        let id: ArgumentId = (&lit).into();
77        assert_eq!(id.as_str(), "¬deny_claim");
78    }
79
80    #[test]
81    fn two_same_literals_produce_equal_ids() {
82        let a = ArgumentId::from(&Literal::atom("x"));
83        let b = ArgumentId::from(&Literal::atom("x"));
84        assert_eq!(a, b);
85    }
86
87    #[test]
88    fn display_matches_as_str() {
89        let id = ArgumentId::new("foo");
90        assert_eq!(format!("{}", id), "foo");
91    }
92}