@@ -726,18 +726,23 @@ enum ShowCommands {
726726 #[ structopt( name = "user-cache-dir" ) ]
727727 /// Print the location of the default per-user cache directory
728728 UserCacheDir ( ShowUserCacheDirCommand ) ,
729+
730+ /// Show metadata from the current environment
731+ Metadata ( ShowMetadataCommand ) ,
729732}
730733
731734impl ShowCommand {
732735 fn customize ( & self , cc : & mut CommandCustomizations ) {
733736 match & self . command {
734737 ShowCommands :: UserCacheDir ( c) => c. customize ( cc) ,
738+ ShowCommands :: Metadata ( c) => c. customize ( cc) ,
735739 }
736740 }
737741
738742 fn execute ( self , config : PersistentConfig , status : & mut dyn StatusBackend ) -> Result < i32 > {
739743 match self . command {
740744 ShowCommands :: UserCacheDir ( c) => c. execute ( config, status) ,
745+ ShowCommands :: Metadata ( c) => c. execute ( config, status) ,
741746 }
742747 }
743748}
@@ -757,3 +762,141 @@ impl ShowUserCacheDirCommand {
757762 Ok ( 0 )
758763 }
759764}
765+
766+ #[ derive( Debug , Eq , PartialEq , StructOpt ) ]
767+ struct ShowMetadataCommand {
768+ #[ structopt( help = "The metadata key to get" ) ]
769+ key : Option < String > ,
770+ }
771+
772+
773+ // If parts is none, print this value as a string.
774+ // This is used to recursively stringify arrays and tables.
775+ fn get_metadata ( meta : & toml:: Value , mut parts : Option < core:: str:: Split < & str > > ) -> Option < String > {
776+ match meta {
777+ toml:: Value :: String ( _)
778+ | toml:: Value :: Boolean ( _)
779+ | toml:: Value :: Integer ( _)
780+ | toml:: Value :: Float ( _)
781+ | toml:: Value :: Datetime ( _) => {
782+ if parts. is_some ( ) && parts. unwrap ( ) . next ( ) . is_some ( ) {
783+ // None of these types may be indexed further
784+ return None ;
785+ }
786+
787+ return Some ( match meta {
788+ toml:: Value :: String ( s) => s. clone ( ) ,
789+ toml:: Value :: Datetime ( d) => format ! ( "{}" , d) ,
790+ _ => format ! ( "{}" , meta) ,
791+ } ) ;
792+ }
793+
794+ toml:: Value :: Array ( a) => {
795+ let idx = {
796+ if parts. is_some ( ) {
797+ parts. as_mut ( ) . unwrap ( ) . next ( )
798+ } else {
799+ None
800+ }
801+ } ;
802+
803+ match idx {
804+ None => {
805+ // Print the whole array
806+ let s = a
807+ . iter ( )
808+ . map ( |x| get_metadata ( x, None ) . unwrap ( ) )
809+ . collect :: < Vec < String > > ( )
810+ . join ( ", " ) ;
811+ return Some ( format ! ( "[{}]" , s) ) ;
812+ }
813+ Some ( s) => {
814+ if s == "len" {
815+ // Magic key to get array length
816+ return Some ( format ! ( "{}" , a. len( ) ) ) ;
817+ } else {
818+ let i: usize = match s. parse ( ) {
819+ Ok ( i) => i,
820+ Err ( _) => return None ,
821+ } ;
822+
823+ let meta = match a. get ( i) {
824+ Some ( v) => v,
825+ None => return None ,
826+ } ;
827+
828+ return get_metadata ( meta, parts) ;
829+ }
830+ }
831+ }
832+ }
833+
834+ toml:: Value :: Table ( t) => {
835+ let idx = {
836+ if parts. is_some ( ) {
837+ parts. as_mut ( ) . unwrap ( ) . next ( )
838+ } else {
839+ None
840+ }
841+ } ;
842+
843+ match idx {
844+ None => return None ,
845+ Some ( s) => {
846+ let meta = match t. get ( s) {
847+ Some ( v) => v,
848+ None => return None ,
849+ } ;
850+
851+ return get_metadata ( meta, parts) ;
852+ }
853+ }
854+ }
855+ } ;
856+ }
857+
858+
859+
860+ impl ShowMetadataCommand {
861+ fn customize ( & self , cc : & mut CommandCustomizations ) {
862+ cc. always_stderr = true ;
863+ }
864+
865+ fn execute ( mut self , _config : PersistentConfig , status : & mut dyn StatusBackend ) -> Result < i32 > {
866+ // `tectonic show metadata` should be shell-script-friendly.
867+ // Namely,it should either print a metadata value or return error code 1,
868+ // with NO OTHER OUTPUT.
869+
870+ let meta = match Workspace :: open_from_environment ( ) {
871+ Ok ( ws) => {
872+ let doc = ws. first_document ( ) ;
873+ let meta = doc. metadata . clone ( ) ;
874+ if meta. is_none ( ) {
875+ // This workspace has no metadata.
876+ return Ok ( 1 ) ;
877+ }
878+ meta. unwrap ( )
879+ }
880+
881+ Err ( _) => {
882+ //tt_error!(status, "could not load metadata. Are you in a workspace?");
883+ return Ok ( 1 ) ;
884+ }
885+ } ;
886+
887+ let parts;
888+ if self . key . is_none ( ) {
889+ parts = None ;
890+ } else {
891+ parts = Some ( self . key . as_mut ( ) . unwrap ( ) . split ( "." ) ) ;
892+ }
893+
894+ let meta = get_metadata ( & meta, parts) ;
895+ if meta. is_none ( ) {
896+ return Ok ( 1 ) ;
897+ }
898+
899+ println ! ( "{}" , meta. unwrap( ) ) ;
900+ Ok ( 0 )
901+ }
902+ }
0 commit comments