github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/cmd/render.go (about) 1 package cmd 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 9 "github.com/qri-io/ioes" 10 "github.com/qri-io/qri/dsref" 11 qerr "github.com/qri-io/qri/errors" 12 "github.com/qri-io/qri/lib" 13 "github.com/spf13/cobra" 14 ) 15 16 // NewRenderCommand creates a new `qri render` command for executing templates against datasets 17 func NewRenderCommand(f Factory, ioStreams ioes.IOStreams) *cobra.Command { 18 o := &RenderOptions{IOStreams: ioStreams} 19 cmd := &cobra.Command{ 20 Use: "render", 21 Short: "render a dataset readme or a dataset template", 22 Long: `Render a dataset either by converting its readme from markdown to 23 html, or by filling in a template using the go/html template style. 24 25 Use the ` + "`--output`" + ` flag to save the rendered html to a file. 26 27 Use the ` + "`--viz`" + ` flag to render the viz. Default is to use readme. 28 29 Use the ` + "`--template`" + ` flag to use a custom template. If no template is 30 provided, Qri will render the dataset with a default template.`, 31 Example: ` # Render the readme of a dataset called me/schools: 32 $ qri render -o=schools.html me/schools 33 34 # Render a dataset with a custom template: 35 $ qri render --viz --template=template.html me/schools`, 36 Annotations: map[string]string{ 37 "group": "dataset", 38 }, 39 RunE: func(cmd *cobra.Command, args []string) error { 40 if err := o.Complete(f, args); err != nil { 41 return err 42 } 43 return o.Run() 44 }, 45 } 46 47 cmd.Flags().StringVarP(&o.Template, "template", "t", "", "path to template file") 48 cmd.MarkFlagFilename("template") 49 cmd.Flags().BoolVarP(&o.UseViz, "viz", "v", false, "whether to use the viz component") 50 cmd.Flags().StringVarP(&o.Output, "output", "o", "", "path to write output file") 51 cmd.MarkFlagFilename("output") 52 53 return cmd 54 } 55 56 // RenderOptions encapsulates state for the render command 57 type RenderOptions struct { 58 ioes.IOStreams 59 60 Refs *RefSelect 61 Template string 62 UseViz bool 63 Output string 64 65 inst *lib.Instance 66 } 67 68 // Complete adds any missing configuration that can only be added just before calling Run 69 func (o *RenderOptions) Complete(f Factory, args []string) (err error) { 70 if o.inst, err = f.Instance(); err != nil { 71 return err 72 } 73 if o.Refs, err = GetCurrentRefSelect(f, args, 1); err != nil { 74 return err 75 } 76 return nil 77 } 78 79 // Run executes the render command 80 func (o *RenderOptions) Run() error { 81 // NOTE: `--viz` is required even if we could infer it from `--template` in 82 // order to make extra sure the user is not mixing up possible args when 83 // rendering the readme. 84 if o.Template != "" && !o.UseViz { 85 return fmt.Errorf("you must specify --viz when using --template") 86 } 87 88 p := &lib.RenderParams{} 89 var err error 90 if o.UseViz { 91 p, err = o.vizRenderParams() 92 if err != nil { 93 return err 94 } 95 } else { 96 p = o.readmeRenderParams() 97 } 98 99 res, err := o.inst.Dataset().Render(context.TODO(), p) 100 if err != nil { 101 if errors.Is(err, dsref.ErrEmptyRef) { 102 return qerr.New(err, "peername and dataset name needed in order to render, for example:\n $ qri render me/dataset_name\nsee `qri render --help` from more info") 103 } 104 return err 105 } 106 107 if o.Output == "" { 108 fmt.Fprint(o.Out, string(res)) 109 } else { 110 ioutil.WriteFile(o.Output, res, 0777) 111 } 112 return nil 113 } 114 115 func (o *RenderOptions) vizRenderParams() (p *lib.RenderParams, err error) { 116 var template []byte 117 if o.Template != "" { 118 template, err = ioutil.ReadFile(o.Template) 119 if err != nil { 120 return nil, err 121 } 122 } 123 124 return &lib.RenderParams{ 125 Ref: o.Refs.Ref(), 126 Template: template, 127 Format: "html", 128 Selector: "viz", 129 }, nil 130 } 131 132 func (o *RenderOptions) readmeRenderParams() *lib.RenderParams { 133 return &lib.RenderParams{ 134 Ref: o.Refs.Ref(), 135 Format: "html", 136 Selector: "readme", 137 } 138 }