@@ -15,9 +15,10 @@ use ::toml as serde_toml;
15
15
pub enum Format {
16
16
Json ,
17
17
Toml ,
18
+ Yaml ,
18
19
}
19
20
20
- pub const POSSIBLE_FORMATS : & [ & str ] = & [ "json" , "toml" ] ;
21
+ pub const POSSIBLE_FORMATS : & [ & str ] = & [ "json" , "toml" , "yaml" ] ;
21
22
22
23
impl std:: fmt:: Display for Format {
23
24
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
@@ -27,6 +28,7 @@ impl std::fmt::Display for Format {
27
28
match self {
28
29
Format :: Json => "json" ,
29
30
Format :: Toml => "toml" ,
31
+ Format :: Yaml => "yaml" ,
30
32
}
31
33
)
32
34
}
@@ -48,6 +50,8 @@ impl FromStr for Format {
48
50
Ok ( Format :: Json )
49
51
} else if s == "toml" {
50
52
Ok ( Format :: Toml )
53
+ } else if s == "yaml" || s == "yml" {
54
+ Ok ( Format :: Yaml )
51
55
} else {
52
56
Err ( ParseFormatError :: NoSuchFormat ( s) )
53
57
}
@@ -78,6 +82,13 @@ impl Format {
78
82
fs_from_value ( v, & config, & mut inodes) ;
79
83
info ! ( "done" ) ;
80
84
}
85
+ Format :: Yaml => {
86
+ info ! ( "reading toml value" ) ;
87
+ let v = yaml:: from_reader ( reader) . expect ( "YAML" ) ;
88
+ info ! ( "building inodes" ) ;
89
+ fs_from_value ( v, & config, & mut inodes) ;
90
+ info ! ( "done" ) ;
91
+ }
81
92
} ;
82
93
83
94
FS :: new ( inodes, config)
@@ -121,6 +132,14 @@ impl Format {
121
132
toml:: to_writer ( writer, & v) . unwrap ( ) ;
122
133
info ! ( "done" ) ;
123
134
}
135
+ Format :: Yaml => {
136
+ info ! ( "generating yaml value" ) ;
137
+ let v: yaml:: Value = value_from_fs ( fs, fuser:: FUSE_ROOT_ID ) ;
138
+ info ! ( "writing" ) ;
139
+ debug ! ( "outputting {}" , v) ;
140
+ yaml:: to_writer ( writer, & v) . unwrap ( ) ;
141
+ info ! ( "done" ) ;
142
+ }
124
143
}
125
144
}
126
145
}
@@ -389,8 +408,8 @@ mod json {
389
408
390
409
mod toml {
391
410
use super :: * ;
392
-
393
411
use serde_toml:: Value ;
412
+
394
413
#[ derive( Debug ) ]
395
414
pub enum Error < E > {
396
415
Io ( std:: io:: Error ) ,
@@ -485,3 +504,165 @@ mod toml {
485
504
}
486
505
}
487
506
}
507
+
508
+ mod yaml {
509
+ use super :: * ;
510
+ use std:: hash:: { Hash , Hasher } ;
511
+ use yaml_rust:: { EmitError , ScanError , Yaml } ;
512
+
513
+ #[ derive( Clone , Debug ) ]
514
+ pub struct Value ( Yaml ) ;
515
+
516
+ #[ derive( Debug ) ]
517
+ pub enum Error < E > {
518
+ Io ( std:: io:: Error ) ,
519
+ Yaml ( E ) ,
520
+ }
521
+
522
+ pub fn from_reader ( mut reader : Box < dyn std:: io:: Read > ) -> Result < Value , Error < ScanError > > {
523
+ let mut text = String :: new ( ) ;
524
+ let _len = reader. read_to_string ( & mut text) . map_err ( Error :: Io ) ?;
525
+ yaml_rust:: YamlLoader :: load_from_str ( & text)
526
+ . map ( |vs| {
527
+ Value ( if vs. len ( ) == 1 {
528
+ vs. into_iter ( ) . next ( ) . unwrap ( )
529
+ } else {
530
+ Yaml :: Array ( vs)
531
+ } )
532
+ } )
533
+ . map_err ( Error :: Yaml )
534
+ }
535
+
536
+ pub fn to_writer (
537
+ mut writer : Box < dyn std:: io:: Write > ,
538
+ v : & Value ,
539
+ ) -> Result < ( ) , Error < EmitError > > {
540
+ let mut text = String :: new ( ) ;
541
+ let mut emitter = yaml_rust:: YamlEmitter :: new ( & mut text) ;
542
+ emitter. dump ( & v. 0 ) . map_err ( Error :: Yaml ) ?;
543
+ writer. write_all ( text. as_bytes ( ) ) . map_err ( Error :: Io )
544
+ }
545
+
546
+ impl std:: fmt:: Display for Value {
547
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: result:: Result < ( ) , std:: fmt:: Error > {
548
+ let mut emitter = yaml_rust:: YamlEmitter :: new ( f) ;
549
+ emitter. dump ( & self . 0 ) . map_err ( |e| match e {
550
+ yaml_rust:: EmitError :: FmtError ( e) => e,
551
+ yaml_rust:: EmitError :: BadHashmapKey => {
552
+ panic ! ( "unrecoverable YAML display error: BadHashmapKey" )
553
+ }
554
+ } )
555
+ }
556
+ }
557
+
558
+ fn yaml_size ( v : & Yaml ) -> usize {
559
+ match v {
560
+ Yaml :: Real ( _)
561
+ | Yaml :: Integer ( _)
562
+ | Yaml :: String ( _)
563
+ | Yaml :: Boolean ( _)
564
+ | Yaml :: Null
565
+ | Yaml :: BadValue
566
+ | Yaml :: Alias ( _) => 1 ,
567
+ Yaml :: Array ( vs) => vs. iter ( ) . map ( |v| yaml_size ( v) ) . sum :: < usize > ( ) + 1 ,
568
+ Yaml :: Hash ( fvs) => fvs. iter ( ) . map ( |( _, v) | yaml_size ( v) ) . sum :: < usize > ( ) + 1 ,
569
+ }
570
+ }
571
+
572
+ fn yaml_key_to_string ( v : Yaml ) -> String {
573
+ match v {
574
+ Yaml :: Boolean ( b) => format ! ( "{}" , b) ,
575
+ Yaml :: Real ( s) => s,
576
+ Yaml :: Integer ( n) => format ! ( "{}" , n) ,
577
+ Yaml :: String ( s) => s,
578
+ Yaml :: Alias ( n) => format ! ( "alias{}" , n) ,
579
+ Yaml :: Array ( vs) => {
580
+ let mut hasher = std:: collections:: hash_map:: DefaultHasher :: new ( ) ;
581
+ vs. hash ( & mut hasher) ;
582
+ format ! ( "{}" , hasher. finish( ) )
583
+ }
584
+ Yaml :: Hash ( fvs) => {
585
+ let mut hasher = std:: collections:: hash_map:: DefaultHasher :: new ( ) ;
586
+ fvs. hash ( & mut hasher) ;
587
+ format ! ( "{}" , hasher. finish( ) )
588
+ }
589
+ Yaml :: Null => "null" . into ( ) ,
590
+ Yaml :: BadValue => "badvalue" . into ( ) ,
591
+ }
592
+ }
593
+
594
+ impl Nodelike for Value {
595
+ fn kind ( & self ) -> FileType {
596
+ match & self . 0 {
597
+ Yaml :: Array ( _) | Yaml :: Hash ( _) => FileType :: Directory ,
598
+ _ => FileType :: RegularFile ,
599
+ }
600
+ }
601
+
602
+ fn size ( & self ) -> usize {
603
+ yaml_size ( & self . 0 )
604
+ }
605
+
606
+ fn node ( self , config : & Config ) -> Node < Self > {
607
+ let nl = if config. add_newlines { "\n " } else { "" } ;
608
+
609
+ match self . 0 {
610
+ Yaml :: Null => Node :: String ( "" . into ( ) ) ,
611
+ Yaml :: Boolean ( b) => Node :: Bytes ( format ! ( "{}{}" , b, nl) . into_bytes ( ) ) ,
612
+ Yaml :: Real ( s) => Node :: String ( s + nl) ,
613
+ Yaml :: Integer ( n) => Node :: Bytes ( format ! ( "{}{}" , n, nl) . into_bytes ( ) ) ,
614
+ Yaml :: String ( s) => {
615
+ if config. try_decode_base64 {
616
+ if let Ok ( bytes) = base64:: decode_config ( & s, config. base64 ) {
617
+ return Node :: Bytes ( bytes) ;
618
+ }
619
+ }
620
+
621
+ Node :: String ( if s. ends_with ( '\n' ) { s } else { s + nl } )
622
+ }
623
+ Yaml :: Array ( vs) => Node :: List ( vs. into_iter ( ) . map ( Value ) . collect ( ) ) ,
624
+ Yaml :: Hash ( fvs) => Node :: Map (
625
+ fvs. into_iter ( )
626
+ . map ( |( k, v) | ( yaml_key_to_string ( k) , Value ( v) ) )
627
+ . collect ( ) ,
628
+ ) ,
629
+ // ??? 2021-06-21 support aliases w/hard links?
630
+ Yaml :: Alias ( n) => Node :: Bytes ( format ! ( "alias{}{}" , n, nl) . into_bytes ( ) ) ,
631
+ Yaml :: BadValue => Node :: Bytes ( "bad YAML value" . into ( ) ) ,
632
+ }
633
+ }
634
+
635
+ fn from_string ( contents : String , _config : & Config ) -> Self {
636
+ if contents == "true" {
637
+ Value ( Yaml :: Boolean ( true ) )
638
+ } else if contents == "false" {
639
+ Value ( Yaml :: Boolean ( false ) )
640
+ } else if let Ok ( n) = i64:: from_str ( & contents) {
641
+ Value ( Yaml :: Integer ( n) )
642
+ } else if f64:: from_str ( & contents) . is_ok ( ) {
643
+ Value ( Yaml :: Real ( contents) )
644
+ } else {
645
+ Value ( Yaml :: String ( contents) )
646
+ }
647
+ }
648
+
649
+ fn from_bytes < T > ( contents : T , config : & Config ) -> Self
650
+ where
651
+ T : AsRef < [ u8 ] > ,
652
+ {
653
+ Value ( Yaml :: String ( base64:: encode_config ( contents, config. base64 ) ) )
654
+ }
655
+
656
+ fn from_list_dir ( vs : Vec < Self > , _config : & Config ) -> Self {
657
+ Value ( Yaml :: Array ( vs. into_iter ( ) . map ( |v| v. 0 ) . collect ( ) ) )
658
+ }
659
+
660
+ fn from_named_dir ( fvs : HashMap < String , Self > , config : & Config ) -> Self {
661
+ Value ( Yaml :: Hash (
662
+ fvs. into_iter ( )
663
+ . map ( |( k, v) | ( Value :: from_string ( k, config) . 0 , v. 0 ) )
664
+ . collect ( ) ,
665
+ ) )
666
+ }
667
+ }
668
+ }
0 commit comments