Skip to main content

swc_neuron/
structures.rs

1//! Module for types represented by the structure ID field of a SWC sample.
2//!
3//! Contains some known structure schemas and macros for creating more.
4use std::collections::HashSet;
5use std::convert::TryFrom;
6use std::fmt::Debug;
7
8/// Trait for the structures represented by samples, identified by an integer.
9pub trait StructureIdentifier: Copy + Clone + Debug + TryFrom<isize> + Into<isize> {
10    /// List all the variants which are not catch-alls.
11    fn known_variants() -> &'static [Self];
12
13    /// If the structure field allows for any value, return `None`;
14    /// otherwise, return `Some(HashSet<allowed_values>)`.
15    /// `no_catchall`, if `true`, ignores the "catch-all" variant
16    /// in determining what is allowed, if present.
17    fn allowed_values(no_catchall: bool) -> Option<HashSet<isize>>;
18
19    /// Get a static string description of the structure; None if this is the catch-all variant.
20    fn as_str(&self) -> Option<&'static str>;
21}
22
23/// Macro for creating a public enum implementing StructureIdentifier.
24///
25/// The first argument is the enum's identifier.
26/// Subsequent arguments are given in the form `value = variant "longer name"`:
27/// that is, a signed integer literal, then an equals sign, then
28/// the identifier of the enum variant that literal represents,
29/// then a string literal describing the variant.
30/// There is an optional final argument which is the identifier of a variant
31/// which catches all other values, and stores the value originally given to it.
32///
33/// If the final argument is given, the enum implements From<isize>:
34/// otherwise, it only implements TryFrom<isize>,
35/// returning an error which contains the offending integer.
36#[macro_export]
37macro_rules! structure_mapping {
38    ( $id:ident $(, $val:literal = $name:ident $desc:expr )* $(,)?) => {
39        #[derive(Copy, Clone, Debug)]
40        #[allow(missing_docs)]
41        pub enum $id {
42            $( $name, )+
43        }
44
45        impl TryFrom<isize> for $id {
46            type Error = isize;
47
48            fn try_from(val: isize) -> Result<Self, Self::Error> {
49                match val {
50                    $( $val => Ok(Self::$name), )*
51                    value => Err(value),
52                }
53            }
54        }
55
56        impl From<$id> for isize {
57            fn from(val: $id) -> Self {
58                match val {
59                    $( $id::$name => $val, )*
60                }
61            }
62        }
63
64        impl StructureIdentifier for $id {
65            fn known_variants() -> &'static [Self] {
66                &[
67                    $( Self::$name, )*
68                ]
69            }
70
71            fn allowed_values(_no_catchall: bool) -> Option<HashSet<isize>> {
72                Some(vec![$( $val, )*].into_iter().collect())
73            }
74
75            fn as_str(&self) -> Option<&'static str> {
76                match self {
77                    $( Self::$name => Some($desc), )*
78                }
79            }
80        }
81
82    };
83    ( $id:ident $(, $val:literal = $name:ident $desc:expr )*, $othername:ident $(,)?) => {
84        #[derive(Copy, Clone, Debug)]
85        #[allow(missing_docs)]
86        pub enum $id {
87            $( $name, )*
88            $othername(isize),
89        }
90
91        impl From<isize> for $id {
92            fn from(val: isize) -> Self {
93                match val {
94                    $( $val => Self::$name, )*
95                    x => Self::$othername(x),
96                }
97            }
98        }
99
100        impl From<$id> for isize {
101            fn from(val: $id) -> Self {
102                match val {
103                    $( $id::$name => $val, )*
104                    $id::$othername(x) => x,
105                }
106            }
107        }
108
109        impl StructureIdentifier for $id {
110            fn known_variants() -> &'static [Self] {
111                &[
112                    $( Self::$name, )*
113                ]
114            }
115
116            fn allowed_values(no_catchall: bool) -> Option<HashSet<isize>> {
117                if no_catchall {
118                    Some(vec![$( $val, )*].into_iter().collect())
119                } else {
120                    None
121                }
122            }
123
124            fn as_str(&self) -> Option<&'static str> {
125                match self {
126                    $( Self::$name => Some($desc), )*
127                    Self::$othername(_) => None,
128                }
129            }
130        }
131
132    };
133}
134
135/// Thin wrapper around the `structure_mapping` macro for producing enums
136/// which extend the Neuromorpho SWC standard (i.e. have additional structure types in the `Custom` block, 5+).
137///
138/// Again, you can either allow or disallow the catch-all case.
139#[macro_export]
140macro_rules! neuromorpho_ext {
141    ( $id:ident $(, $val:literal = $name:ident $desc:expr )* $(,)?) => {
142        structure_mapping!(
143            $id,
144            -1 = Root "root",
145            0 = Undefined "undefined",
146            1 = Soma "soma",
147            2 = Axon "axon",
148            3 = BasalDendrite "basal dendrite",
149            4 = ApicalDendrite "apical dendrite",
150            $($val = $name $desc, )*
151        );
152    };
153    ( $id:ident $(, $val:literal = $name:ident $desc:expr )*, $othername:ident $(,)?) => {
154        structure_mapping!(
155            $id,
156            -1 = Root "root",
157            0 = Undefined "undefined",
158            1 = Soma "soma",
159            2 = Axon "axon",
160            3 = BasalDendrite "basal dendrite",
161            4 = ApicalDendrite "apical dendrite",
162            $($val = $name $desc, )*
163            $othername,
164        );
165    };
166}
167
168neuromorpho_ext!(NeuromorphoStructure, Custom);
169neuromorpho_ext!(CnicStructure, 5 = ForkPoint "fork point", 6 = EndPoint "end point", 7 = Custom "custom");
170neuromorpho_ext!(
171    NavisStructure,
172    5 = ForkPoint "fork point",
173    6 = EndPoint "end point",
174    7 = Presynapse "presynapse",
175    8 = Postsynapse "postsynapse",
176    9 = PreAndPostsynapse "pre and postsynapse",
177    Custom,
178);
179neuromorpho_ext!(VnedStructure, 10 = SomaRelated "soma related", Custom);
180
181structure_mapping!(GulyasStructure, IsoDiameterStructure);
182structure_mapping!(AnyStructure, Any);