github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/cmd/present/dir.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"html/template"
     9  	"io"
    10  	"log"
    11  	"net/http"
    12  	"os"
    13  	"path/filepath"
    14  	"sort"
    15  
    16  	"golang.org/x/tools/present"
    17  )
    18  
    19  func init() {
    20  	http.HandleFunc("/", dirHandler)
    21  }
    22  
    23  // dirHandler serves a directory listing for the requested path, rooted at basePath.
    24  func dirHandler(w http.ResponseWriter, r *http.Request) {
    25  	if r.URL.Path == "/favicon.ico" {
    26  		http.Error(w, "not found", 404)
    27  		return
    28  	}
    29  	const base = "."
    30  	name := filepath.Join(base, r.URL.Path)
    31  	if isDoc(name) {
    32  		err := renderDoc(w, name)
    33  		if err != nil {
    34  			log.Println(err)
    35  			http.Error(w, err.Error(), 500)
    36  		}
    37  		return
    38  	}
    39  	if isDir, err := dirList(w, name); err != nil {
    40  		log.Println(err)
    41  		http.Error(w, err.Error(), 500)
    42  		return
    43  	} else if isDir {
    44  		return
    45  	}
    46  	http.FileServer(http.Dir(base)).ServeHTTP(w, r)
    47  }
    48  
    49  func isDoc(path string) bool {
    50  	_, ok := contentTemplate[filepath.Ext(path)]
    51  	return ok
    52  }
    53  
    54  var (
    55  	// dirListTemplate holds the front page template.
    56  	dirListTemplate *template.Template
    57  
    58  	// contentTemplate maps the presentable file extensions to the
    59  	// template to be executed.
    60  	contentTemplate map[string]*template.Template
    61  )
    62  
    63  func initTemplates(base string) error {
    64  	// Locate the template file.
    65  	actionTmpl := filepath.Join(base, "templates/action.tmpl")
    66  
    67  	contentTemplate = make(map[string]*template.Template)
    68  
    69  	for ext, contentTmpl := range map[string]string{
    70  		".slide":   "slides.tmpl",
    71  		".article": "article.tmpl",
    72  	} {
    73  		contentTmpl = filepath.Join(base, "templates", contentTmpl)
    74  
    75  		// Read and parse the input.
    76  		tmpl := present.Template()
    77  		tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
    78  		if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
    79  			return err
    80  		}
    81  		contentTemplate[ext] = tmpl
    82  	}
    83  
    84  	var err error
    85  	dirListTemplate, err = template.ParseFiles(filepath.Join(base, "templates/dir.tmpl"))
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  // renderDoc reads the present file, gets its template representation,
    94  // and executes the template, sending output to w.
    95  func renderDoc(w io.Writer, docFile string) error {
    96  	// Read the input and build the doc structure.
    97  	doc, err := parse(docFile, 0)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	// Find which template should be executed.
   103  	tmpl := contentTemplate[filepath.Ext(docFile)]
   104  
   105  	// Execute the template.
   106  	return doc.Render(w, tmpl)
   107  }
   108  
   109  func parse(name string, mode present.ParseMode) (*present.Doc, error) {
   110  	f, err := os.Open(name)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	defer f.Close()
   115  	return present.Parse(f, name, 0)
   116  }
   117  
   118  // dirList scans the given path and writes a directory listing to w.
   119  // It parses the first part of each .slide file it encounters to display the
   120  // presentation title in the listing.
   121  // If the given path is not a directory, it returns (isDir == false, err == nil)
   122  // and writes nothing to w.
   123  func dirList(w io.Writer, name string) (isDir bool, err error) {
   124  	f, err := os.Open(name)
   125  	if err != nil {
   126  		return false, err
   127  	}
   128  	defer f.Close()
   129  	fi, err := f.Stat()
   130  	if err != nil {
   131  		return false, err
   132  	}
   133  	if isDir = fi.IsDir(); !isDir {
   134  		return false, nil
   135  	}
   136  	fis, err := f.Readdir(0)
   137  	if err != nil {
   138  		return false, err
   139  	}
   140  	d := &dirListData{Path: name}
   141  	for _, fi := range fis {
   142  		// skip the golang.org directory
   143  		if name == "." && fi.Name() == "golang.org" {
   144  			continue
   145  		}
   146  		e := dirEntry{
   147  			Name: fi.Name(),
   148  			Path: filepath.ToSlash(filepath.Join(name, fi.Name())),
   149  		}
   150  		if fi.IsDir() && showDir(e.Name) {
   151  			d.Dirs = append(d.Dirs, e)
   152  			continue
   153  		}
   154  		if isDoc(e.Name) {
   155  			if p, err := parse(e.Path, present.TitlesOnly); err != nil {
   156  				log.Println(err)
   157  			} else {
   158  				e.Title = p.Title
   159  			}
   160  			switch filepath.Ext(e.Path) {
   161  			case ".article":
   162  				d.Articles = append(d.Articles, e)
   163  			case ".slide":
   164  				d.Slides = append(d.Slides, e)
   165  			}
   166  		} else if showFile(e.Name) {
   167  			d.Other = append(d.Other, e)
   168  		}
   169  	}
   170  	if d.Path == "." {
   171  		d.Path = ""
   172  	}
   173  	sort.Sort(d.Dirs)
   174  	sort.Sort(d.Slides)
   175  	sort.Sort(d.Articles)
   176  	sort.Sort(d.Other)
   177  	return true, dirListTemplate.Execute(w, d)
   178  }
   179  
   180  // showFile reports whether the given file should be displayed in the list.
   181  func showFile(n string) bool {
   182  	switch filepath.Ext(n) {
   183  	case ".pdf":
   184  	case ".html":
   185  	case ".go":
   186  	default:
   187  		return isDoc(n)
   188  	}
   189  	return true
   190  }
   191  
   192  // showDir reports whether the given directory should be displayed in the list.
   193  func showDir(n string) bool {
   194  	if len(n) > 0 && (n[0] == '.' || n[0] == '_') || n == "present" {
   195  		return false
   196  	}
   197  	return true
   198  }
   199  
   200  type dirListData struct {
   201  	Path                          string
   202  	Dirs, Slides, Articles, Other dirEntrySlice
   203  }
   204  
   205  type dirEntry struct {
   206  	Name, Path, Title string
   207  }
   208  
   209  type dirEntrySlice []dirEntry
   210  
   211  func (s dirEntrySlice) Len() int           { return len(s) }
   212  func (s dirEntrySlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   213  func (s dirEntrySlice) Less(i, j int) bool { return s[i].Name < s[j].Name }