<commit_before><commit_msg>Steps towards more flexible godoc: The Mapping object implements a flexible mapping of relative to absolute paths and vice versa.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements the Mapping data structure.

package main

import (
	"fmt"
	"io"
	"os"
	pathutil "path"
	"strings"
)


// A Mapping object maps relative paths (e.g. from URLs)
// to absolute paths (of the file system) and vice versa.
//
// A Mapping object consists of a list of individual mappings
// of the form: prefix -> path which are interpreted as follows:
// A relative path of the form prefix/tail is to be mapped to
// the absolute path/tail, if that absolute path exists in the file
// system. Given a Mapping object, a relative path is mapped to an
// absolute path by trying each of the individual mappings in order,
// until a valid mapping is found. For instance, for the mapping:
//
//	user   -> /home/user
//      public -> /home/user/public
//	public -> /home/build/public
//
// the relative paths below are mapped to absolute paths as follows:
//
//	user/foo                -> /home/user/foo
//      public/net/rpc/file1.go -> /home/user/public/net/rpc/file1.go
//
// If there is no /home/user/public/net/rpc/file2.go, the next public
// mapping entry is used to map the relative path to:
//
//	public/net/rpc/file2.go -> /home/build/public/net/rpc/file2.go
//
// (assuming that file exists).
//
type Mapping struct {
	list []mapping
}


type mapping struct {
	prefix, path string
}


// Init initializes the Mapping from a list of ':'-separated
// paths. Empty paths are ignored; relative paths are assumed
// to be relative to the current working directory and converted
// to absolute paths. For each path of the form:
//
//	dirname/localname
//
// a mapping
//
//	localname -> path
//
// is added to the Mapping object, in the order of occurrence.
// For instance, the argument:
//
//	/home/user:/home/build/public
//
// leads to the following mapping:
//
//	user   -> /home/user
//      public -> /home/build/public
//
func (m *Mapping) Init(paths string) {
	cwd, _ := os.Getwd() // ignore errors

	pathlist := strings.Split(paths, ":", 0)

	list := make([]mapping, len(pathlist))
	n := 0 // number of mappings

	for _, path := range pathlist {
		if len(path) == 0 {
			// ignore empty paths (don't assume ".")
			continue
		}

		// len(path) > 0: normalize path
		if path[0] != '/' {
			path = pathutil.Join(cwd, path)
		} else {
			path = pathutil.Clean(path)
		}

		// check if mapping exists already
		var i int
		for i = 0; i < n; i++ {
			if path == list[i].path {
				break
			}
		}

		// add mapping if it is new
		if i >= n {
			_, prefix := pathutil.Split(path)
			list[i] = mapping{prefix, path}
			n++
		}
	}

	m.list = list[0:n]
}


// IsEmpty returns true if there are no mappings specified.
func (m *Mapping) IsEmpty() bool { return len(m.list) == 0 }


// Fprint prints the mapping.
func (m *Mapping) Fprint(w io.Writer) {
	for _, e := range m.list {
		fmt.Fprintf(w, "\t%s -> %s\n", e.prefix, e.path)
	}
}


func split(path string) (head, tail string) {
	i := strings.Index(path, "/")
	if i > 0 {
		// 0 < i < len(path)
		return path[0:i], path[i+1:]
	}
	return "", path
}


// ToAbsolute maps a relative path to an absolute path using the Mapping
// specified by the receiver. If the path cannot be mapped, the empty
// string is returned.
//
func (m *Mapping) ToAbsolute(path string) string {
	for _, e := range m.list {
		if strings.HasPrefix(path, e.path) {
			// /absolute/prefix/foo -> prefix/foo
			return pathutil.Join(e.prefix, path[len(e.path):]) // Join will remove a trailing '/'
		}
	}
	return "" // no match
}


// ToRelative maps an absolute path to a relative path using the Mapping
// specified by the receiver. If the path cannot be mapped, the empty
// string is returned.
//
func (m *Mapping) ToRelative(path string) string {
	prefix, tail := split(path)
	for _, e := range m.list {
		switch {
		case e.prefix == prefix:
			// use tail
		case e.prefix == "":
			tail = path
		default:
			continue // no match
		}
		abspath := pathutil.Join(e.path, tail)
		if _, err := os.Stat(abspath); err == nil {
			return abspath
		}
	}

	return "" // no match
}
{'fraction_non_alphanumeric': 0.1340538054725224, 'fraction_numerical': 0.004828696252011957, 'mean_word_length': 2.6895674300254453, 'pattern_counts': {'":': 2, '<': 6, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4496724', 'n_tokens_mistral': 1492, 'n_tokens_neox': 1387, 'n_words': 538}