@@ -6,6 +6,7 @@ use std::{
66 ffi:: OsStr ,
77 fs:: { read_dir, read_to_string, write} ,
88 io:: Write ,
9+ ops:: Range ,
910 path:: Path ,
1011 process:: { Command , ExitStatus } ,
1112 str:: FromStr ,
@@ -181,19 +182,83 @@ fn shellcheck() {
181182}
182183
183184#[ test]
184- fn sort ( ) {
185+ fn dependencies_are_sorted ( ) {
185186 for entry in WalkDir :: new ( ".." )
186187 . into_iter ( )
187188 . filter_map ( Result :: ok)
188189 . filter ( |e| e. file_name ( ) == OsStr :: new ( "Cargo.toml" ) )
189190 {
190- let dir = entry. path ( ) . parent ( ) . unwrap ( ) ;
191- Command :: new ( "cargo" )
192- . args ( [ "sort" , "--check" , "--grouped" , "--no-format" ] )
193- . current_dir ( dir)
194- . logged_assert ( )
195- . success ( ) ;
191+ let path = entry. path ( ) ;
192+ let contents = read_to_string ( path) . unwrap ( ) ;
193+ let document = contents. parse :: < toml_edit:: Document < _ > > ( ) . unwrap ( ) ;
194+ for table_name in [ "dependencies" , "dev-dependencies" , "build-dependencies" ] {
195+ let Some ( span) = key_value_pair_span ( & document, table_name) else {
196+ continue ;
197+ } ;
198+ assert ! (
199+ key_value_pairs_are_sorted( & document, span) ,
200+ "`{table_name}` in `{}` are not sorted" ,
201+ path. display( )
202+ ) ;
203+ }
204+ }
205+ }
206+
207+ fn key_value_pair_span < S > (
208+ document : & toml_edit:: Document < S > ,
209+ table_name : & str ,
210+ ) -> Option < Range < usize > > {
211+ // smoelius: The table might not exist.
212+ let item = document. get ( table_name) ?;
213+ let table = item. as_table ( ) . unwrap ( ) ;
214+ // smoelius: The table might exist but be empty.
215+ let ( _, last_item) = table. iter ( ) . last ( ) ?;
216+ let header_span = table. span ( ) . unwrap ( ) ;
217+ let last_item_span = last_item. span ( ) . unwrap ( ) ;
218+ Some ( header_span. end ..last_item_span. end )
219+ }
220+
221+ fn key_value_pairs_are_sorted < S : AsRef < str > > (
222+ document : & toml_edit:: Document < S > ,
223+ span : Range < usize > ,
224+ ) -> bool {
225+ for group in groups ( document, span) {
226+ let pairs = & document. raw ( ) [ group]
227+ . parse :: < toml_edit:: Document < _ > > ( )
228+ . unwrap ( ) ;
229+ if !pairs. iter ( ) . map ( |( k, _) | k) . is_sorted ( ) {
230+ return false ;
231+ }
196232 }
233+ true
234+ }
235+
236+ fn groups < S : AsRef < str > > (
237+ document : & toml_edit:: Document < S > ,
238+ span : Range < usize > ,
239+ ) -> Vec < Range < usize > > {
240+ let group_starts = group_starts ( document, & span) ;
241+ let mut groups = Vec :: with_capacity ( group_starts. len ( ) + 1 ) ;
242+ let mut start = span. start ;
243+ for partition in group_starts {
244+ groups. push ( start..partition) ;
245+ start = partition;
246+ }
247+ groups. push ( start..span. end ) ;
248+ groups
249+ }
250+
251+ /// Find the offsets in `span` that are not newlines, but that are preceded by two (or more)
252+ /// newlines.
253+ fn group_starts < S : AsRef < str > > (
254+ document : & toml_edit:: Document < S > ,
255+ span : & Range < usize > ,
256+ ) -> Vec < usize > {
257+ let raw = & document. raw ( ) [ span. clone ( ) ] . as_bytes ( ) ;
258+ ( 2 ..raw. len ( ) )
259+ . filter ( |& i| raw[ i - 2 ] == b'\n' && raw[ i - 1 ] == b'\n' && raw[ i] != b'\n' )
260+ . map ( |i| span. start + i)
261+ . collect ( )
197262}
198263
199264// smoelius: No other test uses supply_chain.json.
0 commit comments