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  }