...

Source file src/github.com/gabriel-vasile/mimetype/mime.go

Documentation: github.com/gabriel-vasile/mimetype

     1  package mimetype
     2  
     3  import (
     4  	"mime"
     5  
     6  	"github.com/gabriel-vasile/mimetype/internal/charset"
     7  	"github.com/gabriel-vasile/mimetype/internal/magic"
     8  )
     9  
    10  // MIME struct holds information about a file format: the string representation
    11  // of the MIME type, the extension and the parent file format.
    12  type MIME struct {
    13  	mime      string
    14  	aliases   []string
    15  	extension string
    16  	// detector receives the raw input and a limit for the number of bytes it is
    17  	// allowed to check. It returns whether the input matches a signature or not.
    18  	detector magic.Detector
    19  	children []*MIME
    20  	parent   *MIME
    21  }
    22  
    23  // String returns the string representation of the MIME type, e.g., "application/zip".
    24  func (m *MIME) String() string {
    25  	return m.mime
    26  }
    27  
    28  // Extension returns the file extension associated with the MIME type.
    29  // It includes the leading dot, as in ".html". When the file format does not
    30  // have an extension, the empty string is returned.
    31  func (m *MIME) Extension() string {
    32  	return m.extension
    33  }
    34  
    35  // Parent returns the parent MIME type from the hierarchy.
    36  // Each MIME type has a non-nil parent, except for the root MIME type.
    37  //
    38  // For example, the application/json and text/html MIME types have text/plain as
    39  // their parent because they are text files who happen to contain JSON or HTML.
    40  // Another example is the ZIP format, which is used as container
    41  // for Microsoft Office files, EPUB files, JAR files, and others.
    42  func (m *MIME) Parent() *MIME {
    43  	return m.parent
    44  }
    45  
    46  // Is checks whether this MIME type, or any of its aliases, is equal to the
    47  // expected MIME type. MIME type equality test is done on the "type/subtype"
    48  // section, ignores any optional MIME parameters, ignores any leading and
    49  // trailing whitespace, and is case insensitive.
    50  func (m *MIME) Is(expectedMIME string) bool {
    51  	// Parsing is needed because some detected MIME types contain parameters
    52  	// that need to be stripped for the comparison.
    53  	expectedMIME, _, _ = mime.ParseMediaType(expectedMIME)
    54  	found, _, _ := mime.ParseMediaType(m.mime)
    55  
    56  	if expectedMIME == found {
    57  		return true
    58  	}
    59  
    60  	for _, alias := range m.aliases {
    61  		if alias == expectedMIME {
    62  			return true
    63  		}
    64  	}
    65  
    66  	return false
    67  }
    68  
    69  func newMIME(
    70  	mime, extension string,
    71  	detector magic.Detector,
    72  	children ...*MIME) *MIME {
    73  	m := &MIME{
    74  		mime:      mime,
    75  		extension: extension,
    76  		detector:  detector,
    77  		children:  children,
    78  	}
    79  
    80  	for _, c := range children {
    81  		c.parent = m
    82  	}
    83  
    84  	return m
    85  }
    86  
    87  func (m *MIME) alias(aliases ...string) *MIME {
    88  	m.aliases = aliases
    89  	return m
    90  }
    91  
    92  // match does a depth-first search on the signature tree. It returns the deepest
    93  // successful node for which all the children detection functions fail.
    94  func (m *MIME) match(in []byte, readLimit uint32) *MIME {
    95  	for _, c := range m.children {
    96  		if c.detector(in, readLimit) {
    97  			return c.match(in, readLimit)
    98  		}
    99  	}
   100  
   101  	needsCharset := map[string]func([]byte) string{
   102  		"text/plain": charset.FromPlain,
   103  		"text/html":  charset.FromHTML,
   104  		"text/xml":   charset.FromXML,
   105  	}
   106  	// ps holds optional MIME parameters.
   107  	ps := map[string]string{}
   108  	if f, ok := needsCharset[m.mime]; ok {
   109  		if cset := f(in); cset != "" {
   110  			ps["charset"] = cset
   111  		}
   112  	}
   113  
   114  	return m.cloneHierarchy(ps)
   115  }
   116  
   117  // flatten transforms an hierarchy of MIMEs into a slice of MIMEs.
   118  func (m *MIME) flatten() []*MIME {
   119  	out := []*MIME{m}
   120  	for _, c := range m.children {
   121  		out = append(out, c.flatten()...)
   122  	}
   123  
   124  	return out
   125  }
   126  
   127  // clone creates a new MIME with the provided optional MIME parameters.
   128  func (m *MIME) clone(ps map[string]string) *MIME {
   129  	clonedMIME := m.mime
   130  	if len(ps) > 0 {
   131  		clonedMIME = mime.FormatMediaType(m.mime, ps)
   132  	}
   133  
   134  	return &MIME{
   135  		mime:      clonedMIME,
   136  		aliases:   m.aliases,
   137  		extension: m.extension,
   138  	}
   139  }
   140  
   141  // cloneHierarchy creates a clone of m and all its ancestors. The optional MIME
   142  // parameters are set on the last child of the hierarchy.
   143  func (m *MIME) cloneHierarchy(ps map[string]string) *MIME {
   144  	ret := m.clone(ps)
   145  	lastChild := ret
   146  	for p := m.Parent(); p != nil; p = p.Parent() {
   147  		pClone := p.clone(nil)
   148  		lastChild.parent = pClone
   149  		lastChild = pClone
   150  	}
   151  
   152  	return ret
   153  }
   154  
   155  func (m *MIME) lookup(mime string) *MIME {
   156  	for _, n := range append(m.aliases, m.mime) {
   157  		if n == mime {
   158  			return m
   159  		}
   160  	}
   161  
   162  	for _, c := range m.children {
   163  		if m := c.lookup(mime); m != nil {
   164  			return m
   165  		}
   166  	}
   167  	return nil
   168  }
   169  
   170  // Extend adds detection for a sub-format. The detector is a function
   171  // returning true when the raw input file satisfies a signature.
   172  // The sub-format will be detected if all the detectors in the parent chain return true.
   173  // The extension should include the leading dot, as in ".html".
   174  func (m *MIME) Extend(detector func(raw []byte, limit uint32) bool, mime, extension string, aliases ...string) {
   175  	c := &MIME{
   176  		mime:      mime,
   177  		extension: extension,
   178  		detector:  detector,
   179  		parent:    m,
   180  		aliases:   aliases,
   181  	}
   182  
   183  	mu.Lock()
   184  	m.children = append([]*MIME{c}, m.children...)
   185  	mu.Unlock()
   186  }
   187  

View as plain text