@@ -7,6 +7,118 @@ use netcdf_sys::*;
7
7
8
8
use super :: error;
9
9
10
+ mod sealed {
11
+ pub trait Sealed { }
12
+ }
13
+
14
+ /// Types which can be used to distinguish dimensions
15
+ /// in a netCDF file. This can be `&str` (the normal use case)
16
+ /// or a special identifier when using nested groups.
17
+ ///
18
+ /// This trait is not expected to be implemented elsewhere and is therefore sealed.
19
+ ///
20
+ /// # Examples
21
+ /// (helper function to show consumption of type)
22
+ /// ```rust
23
+ /// # use netcdf::AsNcDimensions;
24
+ /// fn take(d: impl AsNcDimensions) {}
25
+ /// ```
26
+ /// Normally one uses the name of the dimension to specify the dimension
27
+ /// ```rust
28
+ /// # use netcdf::AsNcDimensions;
29
+ /// # fn take(d: impl AsNcDimensions) {}
30
+ /// take(()); // scalar
31
+ /// take("x"); // single dimension
32
+ /// take(["x", "y"]); // multiple dimensions
33
+ /// ```
34
+ /// When working with dimensions across groups, it might be necessary
35
+ /// to use dimension identifiers to get the correct group dimension
36
+ /// ```rust,no_run
37
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
38
+ /// # use netcdf::AsNcDimensions;
39
+ /// # fn take(d: impl AsNcDimensions) {}
40
+ /// let file = netcdf::open("test.nc")?;
41
+ /// let dim = file.dimension("x").expect("File does not contain dimension");
42
+ /// let dimid = dim.identifier();
43
+ ///
44
+ /// take(dimid); // from a dimension identifier
45
+ /// take([dimid, dimid]); // from multiple identifiers
46
+ /// # Ok(()) }
47
+ /// ```
48
+ pub trait AsNcDimensions : sealed:: Sealed {
49
+ /// Convert from a slice of [`&str`]/[`DimensionIdentifier`] to concrete dimensions
50
+ /// which are guaranteed to exist in this file
51
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > ;
52
+ }
53
+
54
+ impl sealed:: Sealed for & [ & str ] { }
55
+ impl AsNcDimensions for & [ & str ] {
56
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
57
+ self . iter ( )
58
+ . map ( |& name| match dimension_from_name ( ncid, name) {
59
+ Ok ( Some ( x) ) => Ok ( x) ,
60
+ Ok ( None ) => Err ( format ! ( "Dimension {name} not found" ) . into ( ) ) ,
61
+ Err ( e) => Err ( e) ,
62
+ } )
63
+ . collect ( )
64
+ }
65
+ }
66
+ impl < const N : usize > sealed:: Sealed for [ & str ; N ] { }
67
+ impl < const N : usize > AsNcDimensions for [ & str ; N ] {
68
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
69
+ self . as_slice ( ) . get_dimensions ( ncid)
70
+ }
71
+ }
72
+ impl < const N : usize > sealed:: Sealed for & [ & str ; N ] { }
73
+ impl < const N : usize > AsNcDimensions for & [ & str ; N ] {
74
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
75
+ self . as_slice ( ) . get_dimensions ( ncid)
76
+ }
77
+ }
78
+
79
+ impl sealed:: Sealed for & [ DimensionIdentifier ] { }
80
+ impl AsNcDimensions for & [ DimensionIdentifier ] {
81
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
82
+ self . iter ( )
83
+ . map ( |dimid| match dimension_from_identifier ( ncid, * dimid) {
84
+ Ok ( Some ( x) ) => Ok ( x) ,
85
+ Ok ( None ) => Err ( "Dimension id does not exist" . into ( ) ) ,
86
+ Err ( e) => Err ( e) ,
87
+ } )
88
+ . collect ( )
89
+ }
90
+ }
91
+ impl < const N : usize > sealed:: Sealed for [ DimensionIdentifier ; N ] { }
92
+ impl < const N : usize > AsNcDimensions for [ DimensionIdentifier ; N ] {
93
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
94
+ self . as_slice ( ) . get_dimensions ( ncid)
95
+ }
96
+ }
97
+ impl < const N : usize > sealed:: Sealed for & [ DimensionIdentifier ; N ] { }
98
+ impl < const N : usize > AsNcDimensions for & [ DimensionIdentifier ; N ] {
99
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
100
+ self . as_slice ( ) . get_dimensions ( ncid)
101
+ }
102
+ }
103
+ impl sealed:: Sealed for ( ) { }
104
+ impl AsNcDimensions for ( ) {
105
+ fn get_dimensions < ' g > ( & self , _ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
106
+ Ok ( Vec :: new ( ) )
107
+ }
108
+ }
109
+ impl sealed:: Sealed for & str { }
110
+ impl AsNcDimensions for & str {
111
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
112
+ ( [ * self ] ) . get_dimensions ( ncid)
113
+ }
114
+ }
115
+ impl sealed:: Sealed for DimensionIdentifier { }
116
+ impl AsNcDimensions for DimensionIdentifier {
117
+ fn get_dimensions < ' g > ( & self , ncid : nc_type ) -> error:: Result < Vec < Dimension < ' g > > > {
118
+ ( [ * self ] ) . get_dimensions ( ncid)
119
+ }
120
+ }
121
+
10
122
/// Represents a netcdf dimension
11
123
#[ derive( Debug , Clone ) ]
12
124
pub struct Dimension < ' g > {
@@ -25,9 +137,23 @@ pub struct DimensionIdentifier {
25
137
pub ( crate ) dimid : nc_type ,
26
138
}
27
139
140
+ impl DimensionIdentifier {
141
+ // Internal netcdf detail, the top 16 bits gives the corresponding
142
+ // file handle. This to ensure dimensions are not added from another
143
+ // file which is unrelated to self
144
+ pub ( crate ) fn belongs_to ( & self , ncid : nc_type ) -> bool {
145
+ self . ncid >> 16 == ncid >> 16
146
+ }
147
+ }
148
+
28
149
#[ allow( clippy:: len_without_is_empty) ]
29
150
impl < ' g > Dimension < ' g > {
30
151
/// Get current length of this dimension
152
+ ///
153
+ /// ## Note
154
+ /// A dimension can be unlimited (growable) and changes size
155
+ /// if putting values to a variable which uses this
156
+ /// dimension
31
157
pub fn len ( & self ) -> usize {
32
158
if let Some ( x) = self . len {
33
159
x. get ( )
@@ -66,7 +192,10 @@ impl<'g> Dimension<'g> {
66
192
}
67
193
68
194
/// Grabs the unique identifier for this dimension, which
69
- /// can be used in `add_variable_from_identifiers`
195
+ /// can be used in [`add_variable`](crate::FileMut::add_variable).
196
+ ///
197
+ /// This is useful when working with nested groups and need
198
+ /// to distinguish between names at different levels.
70
199
pub fn identifier ( & self ) -> DimensionIdentifier {
71
200
self . id
72
201
}
@@ -272,6 +401,47 @@ pub(crate) fn dimension_from_name<'f>(
272
401
} ) )
273
402
}
274
403
404
+ pub ( crate ) fn dimension_from_identifier < ' f > (
405
+ ncid : nc_type ,
406
+ dimid : DimensionIdentifier ,
407
+ ) -> error:: Result < Option < Dimension < ' f > > > {
408
+ if !dimid. belongs_to ( ncid) {
409
+ return Err ( error:: Error :: WrongDataset ) ;
410
+ }
411
+ let dimid = dimid. dimid ;
412
+
413
+ let mut dimlen = 0 ;
414
+ unsafe {
415
+ error:: checked ( super :: with_lock ( || nc_inq_dimlen ( ncid, dimid, & mut dimlen) ) ) . unwrap ( ) ;
416
+ }
417
+ if dimlen != 0 {
418
+ // Have to check if this dimension is unlimited
419
+ let mut nunlim = 0 ;
420
+ unsafe {
421
+ error:: checked ( super :: with_lock ( || {
422
+ nc_inq_unlimdims ( ncid, & mut nunlim, std:: ptr:: null_mut ( ) )
423
+ } ) ) ?;
424
+ }
425
+ if nunlim != 0 {
426
+ let mut unlimdims = Vec :: with_capacity ( nunlim. try_into ( ) ?) ;
427
+ unsafe {
428
+ error:: checked ( super :: with_lock ( || {
429
+ nc_inq_unlimdims ( ncid, std:: ptr:: null_mut ( ) , unlimdims. as_mut_ptr ( ) )
430
+ } ) ) ?;
431
+ }
432
+ unsafe { unlimdims. set_len ( nunlim. try_into ( ) ?) }
433
+ if unlimdims. contains ( & dimid) {
434
+ dimlen = 0 ;
435
+ }
436
+ }
437
+ }
438
+ Ok ( Some ( Dimension {
439
+ len : core:: num:: NonZeroUsize :: new ( dimlen) ,
440
+ id : super :: dimension:: DimensionIdentifier { ncid, dimid } ,
441
+ _group : PhantomData ,
442
+ } ) )
443
+ }
444
+
275
445
pub ( crate ) fn add_dimension_at < ' f > (
276
446
ncid : nc_type ,
277
447
name : & str ,
0 commit comments