@@ -119,3 +119,111 @@ impl_individual_parents!(
119119) ;
120120impl_individual_parents ! ( N , usize , & [ crate :: IndividualId ; N ] , self , self . as_slice( ) ) ;
121121impl_individual_parents ! ( N , usize , [ crate :: IndividualId ; N ] , self , self . as_slice( ) ) ;
122+
123+ mod private {
124+ pub trait NewTypeMarker : TryInto < usize , Error = crate :: TskitError > { }
125+ pub trait TableColumnMarker { }
126+ }
127+
128+ impl private:: NewTypeMarker for crate :: EdgeId { }
129+ impl private:: NewTypeMarker for crate :: NodeId { }
130+ impl private:: NewTypeMarker for crate :: SiteId { }
131+ impl private:: NewTypeMarker for crate :: MutationId { }
132+ impl private:: NewTypeMarker for crate :: MigrationId { }
133+ impl private:: NewTypeMarker for crate :: IndividualId { }
134+ impl private:: NewTypeMarker for crate :: PopulationId { }
135+ #[ cfg( feature = "provenance" ) ]
136+ #[ cfg_attr( doc_cfg, doc( cfg( feature = "provenance" ) ) ) ]
137+ impl private:: NewTypeMarker for crate :: ProvenanceId { }
138+
139+ /// Interface of a non-ragged table column.
140+ ///
141+ /// Unlike slice views of table columns, this API
142+ /// allows indexed via row id types and [`crate::SizeType`].
143+ ///
144+ /// # Notes
145+ ///
146+ /// * This trait is sealed.
147+ ///
148+ /// # For C programmers
149+ ///
150+ /// The `C` programming language allows implicit casts between
151+ /// integer types.
152+ /// This implicit behavior allows one to index a table column
153+ /// using a row id type ([`crate::bindings::tsk_id_t`]) because
154+ /// the compiler will cast it to `size_t`.
155+ ///
156+ /// `rust` does not allow implicit casts, which makes working
157+ /// with table columns as slices awkward.
158+ /// One has to manually cast the id type and the resulting code isn't
159+ /// nice to read.
160+ ///
161+ /// This trait solves that problem by requiring that [`std::ops::Index`]
162+ /// by implemented for types that one would like to use as indexes
163+ /// in the `tskit` world.
164+ pub trait TableColumn < I , T > :
165+ std:: ops:: Index < I , Output = T >
166+ + std:: ops:: Index < usize , Output = T >
167+ + std:: ops:: Index < crate :: SizeType , Output = T >
168+ + private:: TableColumnMarker
169+ {
170+ /// Get the underlying slice
171+ fn as_slice ( & self ) -> & [ T ] ;
172+
173+ /// Get with a table row identifier such as [`crate::NodeId`]
174+ fn get_with_id ( & self , at : I ) -> Option < & T > ;
175+
176+ /// The "standard" get function
177+ fn get ( & self , at : usize ) -> Option < & T > {
178+ self . as_slice ( ) . get ( at)
179+ }
180+
181+ /// Get with [`crate::SizeType`]
182+ fn get_with_size_type ( & self , at : crate :: SizeType ) -> Option < & T > {
183+ self . as_slice ( ) . get ( usize:: try_from ( at) . ok ( ) ?)
184+ }
185+
186+ /// Iterator over the data.
187+ fn iter < ' a , ' b > ( & ' a self ) -> impl Iterator < Item = & ' b T >
188+ where
189+ ' a : ' b ,
190+ T : ' b ,
191+ {
192+ self . as_slice ( ) . iter ( )
193+ }
194+
195+ /// Column length
196+ fn len ( & self ) -> usize {
197+ self . as_slice ( ) . len ( )
198+ }
199+
200+ /// Query if column is empty
201+ fn is_empty ( & self ) -> bool {
202+ self . as_slice ( ) . is_empty ( )
203+ }
204+ }
205+
206+ impl < T > private:: TableColumnMarker for crate :: table_column:: OpaqueTableColumn < ' _ , T > { }
207+
208+ impl < I , T > std:: ops:: Index < I > for crate :: table_column:: OpaqueTableColumn < ' _ , T >
209+ where
210+ I : private:: NewTypeMarker ,
211+ {
212+ type Output = T ;
213+ fn index ( & self , index : I ) -> & Self :: Output {
214+ & self . 0 [ index. try_into ( ) . unwrap ( ) ]
215+ }
216+ }
217+
218+ impl < I , T > TableColumn < I , T > for crate :: table_column:: OpaqueTableColumn < ' _ , T >
219+ where
220+ I : private:: NewTypeMarker ,
221+ {
222+ fn as_slice ( & self ) -> & [ T ] {
223+ self . 0
224+ }
225+
226+ fn get_with_id ( & self , at : I ) -> Option < & T > {
227+ self . 0 . get ( at. try_into ( ) . ok ( ) ?)
228+ }
229+ }
0 commit comments