github.com/kata-containers/tests@v0.0.0-20240307153542-772105b56064/cmd/check-markdown/link.go (about)

     1  //
     2  // Copyright (c) 2019 Intel Corporation
     3  //
     4  // SPDX-License-Identifier: Apache-2.0
     5  //
     6  
     7  package main
     8  
     9  import (
    10  	"errors"
    11  	"os"
    12  	"path/filepath"
    13  	"regexp"
    14  	"strings"
    15  )
    16  
    17  // newLink creates a new Link.
    18  func newLink(doc *Doc, address, description string) (Link, error) {
    19  	l := Link{
    20  		Doc:         doc,
    21  		Address:     address,
    22  		Description: description,
    23  	}
    24  
    25  	err := l.categorise()
    26  	if err != nil {
    27  		return Link{}, err
    28  	}
    29  
    30  	return l, nil
    31  }
    32  
    33  // categorise determines the type of Link.
    34  func (l *Link) categorise() error {
    35  	address := l.Address
    36  
    37  	// markdown file extension with optional link name ("#...")
    38  	const re = `\.md#*.*$`
    39  
    40  	pattern := regexp.MustCompile(re)
    41  
    42  	matched := pattern.MatchString(address)
    43  
    44  	if strings.HasPrefix(address, "http:") {
    45  		l.Type = urlLink
    46  	} else if strings.HasPrefix(address, "https:") {
    47  		l.Type = urlLink
    48  	} else if strings.HasPrefix(address, "mailto:") {
    49  		l.Type = mailLink
    50  	} else if strings.HasPrefix(address, anchorPrefix) {
    51  		l.Type = internalLink
    52  
    53  		// Remove the prefix to make a valid link address
    54  		address = strings.TrimPrefix(address, anchorPrefix)
    55  		l.Address = address
    56  	} else if matched {
    57  		l.Type = externalLink
    58  
    59  		file, _, err := splitLink(address)
    60  		if err != nil {
    61  			return err
    62  		}
    63  
    64  		file, err = l.Doc.linkAddrToPath(file)
    65  		if err != nil {
    66  			return err
    67  		}
    68  
    69  		l.ResolvedPath = file
    70  	} else {
    71  		isREADME, err := l.handleImplicitREADME()
    72  		if err != nil {
    73  			return err
    74  		}
    75  
    76  		if !isREADME {
    77  			// Link must be an external file, but not a markdown file.
    78  			l.Type = externalFile
    79  		}
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  // handleImplicitREADME determines if the specified link is an implicit link
    86  // to a README document.
    87  func (l *Link) handleImplicitREADME() (isREADME bool, err error) {
    88  	const readme = "README.md"
    89  
    90  	address := l.Address
    91  	if address == "" {
    92  		return false, errors.New("need link address")
    93  	}
    94  
    95  	file, err := l.Doc.linkAddrToPath(address)
    96  	if err != nil {
    97  		return false, err
    98  	}
    99  
   100  	// The resolved path should exist as this is a local file.
   101  	st, err := os.Stat(file)
   102  	if err != nil {
   103  		return false, err
   104  	}
   105  
   106  	if !st.IsDir() {
   107  		return false, nil
   108  	}
   109  
   110  	// The file is a directory so try appending the implicit README file
   111  	// and see if that exists.
   112  	resolvedPath := filepath.Join(file, readme)
   113  
   114  	success := fileExists(resolvedPath)
   115  
   116  	if success {
   117  		l.Type = externalLink
   118  		l.ResolvedPath = resolvedPath
   119  	}
   120  
   121  	return success, nil
   122  }