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