Skip to content

core.pathlib #

Pathlib Module

1. Basic Path Creation

import incubaid.herolib.core.pathlib

// Get a basic path object
mut path := pathlib.get('/some/path')

// Create a directory (with parent dirs)
mut dir := pathlib.get_dir(
    path: '/some/dir'
    create: true
)!

// Create/get a file
mut file := pathlib.get_file(
    path: '/some/file.txt'
    create: true
)!

2. Path Properties and Operations

// Get various path forms
abs_path := path.absolute()      // Full absolute path
real_path := path.realpath()     // Resolves symlinks
short_path := path.shortpath()   // Uses ~ for home dir

// Get path components
name := path.name()              // Filename with extension
name_no_ext := path.name_no_ext() // Filename without extension
dir_path := path.path_dir()      // Directory containing the path

// Check path properties
if path.exists() { ///exists }
if path.is_file() { ///is file }
if path.is_dir() { ///is directory }
if path.is_link() { ///is symlink }

3. File Listing and Filtering

3.1 Regex-Based Filtering

import incubaid.herolib.core.pathlib

mut dir := pathlib.get('/some/code/project')

// Include files matching regex pattern (e.g., all V files)
mut v_files := dir.list(
    regex: [r'.*\.v$']
)!

// Multiple regex patterns (OR logic)
mut source_files := dir.list(
    regex: [r'.*\.v$', r'.*\.ts$', r'.*\.go$']
)!

// Exclude certain patterns
mut no_tests := dir.list(
    regex: [r'.*\.v$'],
    regex_ignore: [r'.*_test\.v$']
)!

// Ignore both default patterns and custom ones
mut important_files := dir.list(
    regex: [r'.*\.v$'],
    regex_ignore: [r'.*_test\.v$', r'.*\.bak$']
)!

3.2 Simple String-Based Filtering

import incubaid.herolib.core.pathlib

mut dir := pathlib.get('/some/project')

// Include files/dirs containing string in name
mut config_files := dir.list(
    contains: ['config']
)!

// Multiple contains patterns (OR logic)
mut important := dir.list(
    contains: ['main', 'core', 'config'],
    recursive: true
)!

// Exclude files containing certain strings
mut no_backups := dir.list(
    contains_ignore: ['.bak', '.tmp', '.backup']
)!

// Combine contains with exclude
mut python_but_no_cache := dir.list(
    contains: ['.py'],
    contains_ignore: ['__pycache__', '.pyc']
)!

3.3 Advanced Filtering Options

import incubaid.herolib.core.pathlib

mut dir := pathlib.get('/some/project')

// List only directories
mut dirs := dir.list(
    dirs_only: true,
    recursive: true
)!

// List only files
mut files := dir.list(
    files_only: true,
    recursive: false
)!

// Include symlinks
mut with_links := dir.list(
    regex: [r'.*\.conf$'],
    include_links: true
)!

// Don't ignore hidden files (starting with . or _)
mut all_files := dir.list(
    ignore_default: false,
    recursive: true
)!

// Non-recursive (only in current directory)
mut immediate := dir.list(
    recursive: false
)!

// Access the resulting paths
for path in dirs.paths {
    println('${path.name()}')
}

4. Path Operations on Lists

mut pathlist := dir.list(regex: [r'.*\.tmp$'])!

// Delete all files matching filter
pathlist.delete()!

// Copy all files to destination
pathlist.copy('/backup/location')!

5. Common File Operations

// Empty a directory
mut dir := pathlib.get_dir(
    path: '/some/dir'
    empty: true
)!

// Delete a path
mut path := pathlib.get_dir(
    path: '/path/to/delete'
    delete: true
)!

// Get working directory
mut wd := pathlib.get_wd()

6. Path Scanning with Filters and Executors

Path scanning processes directory trees with custom filter and executor functions.

6.1 Basic Scanner Usage

import incubaid.herolib.core.pathlib
import incubaid.herolib.data.paramsparser

// Define a filter function (return true to continue processing)
fn my_filter(mut path pathlib.Path, mut params paramsparser.Params) !bool {
    // Skip files larger than 1MB
    size := path.size()!
    return size < 1_1000000
}

// Define an executor function (process the file)
fn my_executor(mut path pathlib.Path, mut params paramsparser.Params) !paramsparser.Params {
    if path.is_file() {
        content := path.read()!
        println('Processing: ${path.name()} (${content.len} bytes)')
    }
    return params
}

// Run the scan
mut root := pathlib.get_dir(path: '/source/dir')!
mut params := paramsparser.new_params()
root.scan(mut params, [my_filter], [my_executor])!

6.2 Scanner with Multiple Filters and Executors

import incubaid.herolib.core.pathlib
import incubaid.herolib.data.paramsparser

// Filter 1: Skip hidden files
fn skip_hidden(mut path pathlib.Path, mut params paramsparser.Params) !bool {
    return !path.name().starts_with('.')
}

// Filter 2: Only process V files
fn only_v_files(mut path pathlib.Path, mut params paramsparser.Params) !bool {
    if path.is_file() {
        return path.extension() == 'v'
    }
    return true
}

// Executor 1: Count lines
fn count_lines(mut path pathlib.Path, mut params paramsparser.Params) !paramsparser.Params {
    if path.is_file() {
        content := path.read()!
        lines := content.split_into_lines().len
        params.set('total_lines', (params.get_default('total_lines', '0').int() + lines).str())
    }
    return params
}

// Executor 2: Print file info
fn print_info(mut path pathlib.Path, mut params paramsparser.Params) !paramsparser.Params {
    if path.is_file() {
        size := path.size()!
        println('${path.name()}: ${int(size)} bytes')
    }
    return params
}

// Run scan with all filters and executors
mut root := pathlib.get_dir(path: '/source/code')!
mut params := paramsparser.new_params()
root.scan(mut params, [skip_hidden, only_v_files], [count_lines, print_info])!

total := params.get('total_lines')!
println('Total lines: ${total}')

7. Sub-path Getters and Checkers

// Get a sub-path with name fixing and case-insensitive matching
path.sub_get(name: 'mysub_file.md', name_fix_find: true, name_fix: true)!

// Check if a sub-path exists
path.sub_exists(name: 'my_sub_dir')!

// File operations
path.file_exists('file.txt')              // bool
path.file_exists_ignorecase('File.Txt')   // bool
path.file_get('file.txt')!                // Path
path.file_get_ignorecase('File.Txt')!     // Path
path.file_get_new('new.txt')!             // Get or create

// Directory operations
path.dir_exists('mydir')                  // bool
path.dir_get('mydir')!                    // Path
path.dir_get_new('newdir')!               // Get or create

// Symlink operations
path.link_exists('mylink')                // bool
path.link_get('mylink')!                  // Path

8. Path Object Structure

Each Path object contains:

  • path: The actual path string
  • cat: Category (file/dir/linkfile/linkdir)
  • exist: Existence status (yes/no/unknown)

This provides a safe and convenient API for all file system operations in V.

fn find_common_ancestor #

fn find_common_ancestor(paths_ []string) string

recursively finds the least common ancestor of array of paths . will always return the absolute path (relative gets changed to absolute).

fn find_simple_common_ancestor #

fn find_simple_common_ancestor(paths_ []string) string

same as above but will treat symlinks as if normal links allowing finding relative paths between links as well QUESTION: should we merge with above?

fn get #

fn get(path_ string) Path

gets Path object, will check if it exists, is dir_file, ...

fn get_dir #

fn get_dir(args_ GetArgs) !Path

get a directory, or needs to be created if the dir doesn't exist and is not created, then there will be an error

fn get_file #

fn get_file(args_ GetArgs) !Path

fn get_no_check #

fn get_no_check(path_ string) Path

fn get_wd #

fn get_wd() Path

gets working directory

fn is_image #

fn is_image(path string) bool

fn path_equal #

fn path_equal(a_ string, b_ string) bool

case insentive check on paths

fn path_relative #

fn path_relative(source_ string, linkpath_ string) !string

recalc path between target & source . we only support if source_ is an existing dir, links will not be supported . a0 := pathlib.path_relative('$testpath/a/b/c', '$testpath/a/d.txt') or { panic(err) } . assert a0 == '../../d.txt' . a2 := pathlib.path_relative('$testpath/a/b/c', '$testpath/d.txt') or { panic(err) } . assert a2 == '../../../d.txt' . a8 := pathlib.path_relative('$testpath/a/b/c', '$testpath/a/b/c/d/e/e.txt') or { panic(err) } . assert a8 == 'd/e/e.txt' . symlinks will not be resolved, as it leads to unexpected behaviour

fn rsync #

fn rsync(args_ RsyncArgs) !

flexible tool to sync files from to, does even support ssh . args: .

    source string
    dest string
    delete bool //do we want to delete the destination
 ipaddr_src string //e.g. root@192.168.5.5:33 (can be without root@ or :port)
 ipaddr_dst string //can only use src or dst, not both
    ignore []string //arguments to ignore
 ignore_default bool = true //if set will ignore a common set
 stdout bool = true

.

fn rsync_cmd_options #

fn rsync_cmd_options(args_ RsyncArgs) !string

return the cmd with all rsync arguments . see rsync for usage of args

fn temp_write #

fn temp_write(args_ TMPWriteArgs) !string

write temp file and return path

fn template_write #

fn template_write(template_ string, dest string, overwrite bool) !

template is the text coming from template engine.

enum Category #

enum Category {
	unknown
	file
	dir
	linkdir
	linkfile
}

enum JobErrorType #

enum JobErrorType {
	error
	nodir
	notfound
	wrongtype // asked for dir or file, but found other type
	islink
}

enum UYN #

enum UYN {
	unknown
	yes
	no
}

struct BackupArgs #

@[params]
struct BackupArgs {
pub mut:
	root      string
	dest      string
	overwrite bool
	restore   bool // if we want to find the latest one, if we can't find one then its error
}

struct CopyArgs #

@[params]
struct CopyArgs {
pub mut:
	dest           string // path
	delete         bool   // if true will remove files which are on dest which are not on source
	rsync          bool = true // we use rsync as default
	ssh_target     string   // e.g. root@195.192.213.2:999
	ignore         []string // arguments to ignore e.g. ['*.pyc','*.bak']
	ignore_default bool = true // if set will ignore a common set
}

struct GetArgs #

@[params]
struct GetArgs {
pub mut:
	path      string
	create    bool
	check     bool = true // means will check the dir, link or file exists
	empty     bool // will empty the dir or the file
	delete    bool
	increment bool // will increment filename until free name available (filename1...)
}

struct ListArgs #

@[params]
struct ListArgs {
pub mut:
	// Include if matches any regex pattern
	regex []string
	// Exclude if matches any regex pattern
	regex_ignore []string
	// Include if matches any wildcard pattern (* = any sequence)
	filter []string
	// Exclude if matches any wildcard pattern
	filter_ignore []string
	// Traverse directories recursively
	recursive bool = true
	// Ignore files starting with . and _
	ignore_default bool = true
	// Include symlinks
	include_links bool
	// Return only directories
	dirs_only bool
	// Return only files
	files_only bool
}

struct MoveArgs #

struct MoveArgs {
pub mut:
	dest          string // path
	delete        bool   // if true will remove files which are on dest which are not on source
	chmod_execute bool
}

struct Path #

@[heap]
struct Path {
pub mut:
	path  string
	cat   Category
	exist UYN
}

fn (Path) absolute #

fn (path Path) absolute() string

return absolute path . careful symlinks will not be resolved

fn (Path) backup #

fn (mut path Path) backup(args BackupArgs) !Path

create a backup, will maintain the extension

fn (Path) backup_path #

fn (mut path Path) backup_path(args BackupArgs) !Path

start from existing name and look for name.$nr.$ext, nr need to be unique, ideal for backups if dest "" then will use the directory of the fileitself + "/.backup" e.g. /code/myaccount/despiegk/somedir/test.v if would be backed up to /code/myaccount/despiegk/somedir/.backup/test.1.v root is the start of the dir we process e.g. /code/myaccount/despiegk/somedir/test.v if if source = /code/myaccount/despiegk and dest = /backup then the file will be backed up to /backup/somedir/test.1.v

struct BackupArgs{ root string dest string overwrite bool restore bool //if we want to find the latest one, if we can't find one then its error } if overwrite this means will overwrite the last one in the directory

fn (Path) backups_remove #

fn (mut path Path) backups_remove(args BackupArgs) !

fn (Path) check #

fn (mut path Path) check()

check the inside of pathobject, is like an init function

fn (Path) chmod #

fn (mut path Path) chmod(mode int) !

chmod change file access attributes of path to mode. Octals like 0o600 can be used.

fn (Path) chown #

fn (mut path Path) chown(owner int, group int) !

chown changes the owner and group attributes of path to owner and group.

fn (Path) copy #

fn (mut path Path) copy(args_ CopyArgs) !

copy file,dir is always recursive if ssh_target used then will copy over ssh e.g. . dest needs to be a directory or file . return Path of the destination file or dir .

fn (Path) delete #

fn (mut path Path) delete() !

delete

fn (Path) dir_exists #

fn (mut path Path) dir_exists(tofind string) bool

find dir underneith path, if exists return True

fn (Path) dir_get #

fn (mut path Path) dir_get(tofind string) !Path

find dir underneith path, return as Path

fn (Path) dir_get_new #

fn (mut path Path) dir_get_new(tofind string) !Path

get file, if not exist make new one

fn (Path) empty #

fn (mut path Path) empty() !

remove all content but if dir let the dir exist

fn (Path) exists #

fn (mut path Path) exists() bool

check path exists

fn (Path) expand #

fn (mut path Path) expand(dest string) !Path

uncompress to specified directory . if copy then will keep the original

fn (Path) ext #

fn (mut path Path) ext() !string

extension of file .

fn (Path) extend #

fn (mut path Path) extend(parts ...string) !

extend the path, path stays same, no return if dir, needs to stay dir anything else fails

fn (Path) extend_dir_create #

fn (mut p Path) extend_dir_create(parts ...string) !Path

join parts to a path and return path, returns a new path, create if needed

fn (Path) extend_file #

fn (mut p Path) extend_file(name string) !Path

only works for a dir

fn (Path) extension #

fn (path Path) extension() string

returns extension without .

fn (Path) extension_lower #

fn (path Path) extension_lower() string

returns extension without and all lower case

fn (Path) file_exists #

fn (path Path) file_exists(tofind string) bool

find file underneith dir path, if exists return True

fn (Path) file_exists_ignorecase #

fn (mut path Path) file_exists_ignorecase(tofind string) bool

is case insensitive

fn (Path) file_get #

fn (mut path Path) file_get(tofind string) !Path

find file underneith path, if exists return as Path, otherwise error .

fn (Path) file_get_ignorecase #

fn (mut path Path) file_get_ignorecase(tofind string) !Path

fn (Path) file_get_new #

fn (mut path Path) file_get_new(tofind string) !Path

get file, if not exist make new one

fn (Path) is_dir #

fn (mut path Path) is_dir() bool

fn (Path) is_file #

fn (mut path Path) is_file() bool

is a file but no link

fn (Path) is_image #

fn (path Path) is_image() bool

fn (Path) is_image_jpg_png #

fn (path Path) is_image_jpg_png() bool

fn (Path) list #

fn (mut path Path) list(args_ ListArgs) !PathList

List files and directories with filtering

Parameters:- regex: Include if matches regex pattern (e.g., r'.*\.v$')

  • regex_ignore: Exclude if matches regex pattern
  • filter: Include if matches wildcard pattern (e.g., '*.txt', 'test*', 'config')
  • filter_ignore: Exclude if matches wildcard pattern
  • recursive: Traverse directories (default: true)
  • ignore_default: Ignore files starting with . and _ (default: true)
  • dirs_only: Return only directories
  • files_only: Return only files
  • include_links: Include symlinks in results

Examples: dir.list(regex: [r'..v$'], recursive: true)! dir.list(filter: ['.txt', 'config*'], filter_ignore: ['.bak'])! dir.list(regex: [r'.test.'], regex_ignore: [r'._test.v$'])!

fn (Path) md5 #

fn (mut path Path) md5() ![]u8

calculate md5 in reproducable way for directory as well as large file

fn (Path) md5hex #

fn (mut path Path) md5hex() !string

return in hex format

fn (Path) move #

fn (mut path Path) move(args MoveArgs) !

move to other location

dest           string   // path
delete         bool     // if true will remove files which are on dest which are not on source

fn (Path) moveup #

fn (mut path Path) moveup() !

the path will move itself up 1 level . the e.g. /tmp/rclone/rclone-v1.64.2-linux-amd64/ -> /tmp/rclone

fn (Path) moveup_single_subdir #

fn (mut path Path) moveup_single_subdir() !

the path will move itself up 1 level . e.g. path is /tmp/rclone and there is /tmp/rclone/rclone-v1.64.2-linux-amd64 . that last dir needs to move 1 up

fn (Path) name #

fn (path Path) name() string

returns name with extension

fn (Path) name_ends_with_underscore #

fn (mut path Path) name_ends_with_underscore() bool

fn (Path) name_fix_keepext #

fn (mut path Path) name_fix_keepext() string

return name with all lowercase_special chars done but keep extension

fn (Path) name_fix_no_ext #

fn (mut path Path) name_fix_no_ext() string

fn (Path) name_fix_no_underscore_no_ext #

fn (mut path Path) name_fix_no_underscore_no_ext() string

return name with all lowercase_special chars done and also no extension

fn (Path) name_no_ext #

fn (mut path Path) name_no_ext() string

QUESTION: should this mutate path's name, probably not?

fn (Path) parent #

fn (path Path) parent() !Path

find parent of path

fn (Path) parent_find #

fn (path Path) parent_find(tofind string) !Path

walk upwards starting from path untill dir or file tofind is found works recursive

fn (Path) parent_find_advanced #

fn (path Path) parent_find_advanced(tofind string, stop string) ![]Path

parent_find_advanced walks up the directory tree, collecting all items that match tofind pattern until it encounters an item matching the stop pattern. Both tofind and stop use matcher filter format supporting wildcards:- '*.txt' matches any .txt file

  • 'src*' matches anything starting with 'src'
  • '.git' matches exactly '.git'
  • 'test' matches anything containing 'test'

Returns all found paths before hitting the stop condition. If stop is never found, continues until reaching filesystem root.

Examples: // Find all 'test_.v' files until reaching '.git' directory tests := my_path.parent_find_advanced('test_.v', '.git')!

// Find any 'Makefile*' until hitting 'node_modules' makefiles := my_path.parent_find_advanced('Makefile*', 'node_modules')!

// Find '.md' files until reaching '.git' docs := my_path.parent_find_advanced('.md', '.git')!

fn (Path) path_dir #

fn (mut path Path) path_dir() string

full path of dir

fn (Path) path_get_name_with_underscore #

fn (mut path Path) path_get_name_with_underscore() string

return a path which has name ending with _

fn (Path) path_no_ext #

fn (mut path Path) path_no_ext() string

fn (Path) path_normalize #

fn (mut path Path) path_normalize() !bool

will rewrite the path to lower_case if not the case yet will also remove weird chars if changed will return true the file will be moved to the new location

fn (Path) path_relative #

fn (path Path) path_relative(destpath string) !string

get relative path in relation to destpath . will not resolve symlinks

fn (Path) read #

fn (mut path Path) read() !string

read content from file

fn (Path) readb #

fn (mut path Path) readb() ![]u8

read bytes from file

fn (Path) realpath #

fn (path Path) realpath() string

return absolute path . careful the symlinks will be followed !!!

fn (Path) recursive_text #

fn (mut path Path) recursive_text() ![]string

get all text for path and underneith (works for dir & file)

fn (Path) rename #

fn (mut path Path) rename(name string) !

rename the file or directory

fn (Path) restore #

fn (mut path Path) restore(args BackupArgs) !

fn (Path) rm #

fn (mut path Path) rm() !

delete

fn (Path) scan #

fn (mut path Path) scan(mut parameters paramsparser.Params, filters []Filter0, executors []Executor0) !paramsparser.Params

the filters are function which needs to return true if to process with alle executors . see https://github.com/incubaid/herolib/blob/development/examples/core/pathlib/examples/scanner/path_scanner.v . if any of the filters returns false then we don't continue . if we return True then it means the dir or file is processed . . type Filter0 = fn (mut Path, mut paramsparser.Params) bool type Executor0 = fn (mut Path, mut paramsparser.Params) !paramsparser.Params

fn (Path) sha256 #

fn (mut path Path) sha256() !string

return sha256 hash of a file

fn (Path) shortpath #

fn (path Path) shortpath() string

fn (Path) size #

fn (mut path Path) size() !f64

fn (Path) size_kb #

fn (mut path Path) size_kb() !int

fn (Path) sub_exists #

fn (mut path Path) sub_exists(args_ SubGetParams) !bool

will check if dir exists params: .- name

  • name_fix_find bool :means we will also find if name is same as the name_fix .
  • name_fix bool :if file found and name fix was different than file on filesystem, will rename .
  • dir_ensure bool :if dir_ensure on will fail if its not a dir .
  • file_ensure bool :if file_ensure on will fail if its not a dir .

fn (Path) sub_get #

fn (mut path Path) sub_get(args_ SubGetParams) !Path

will get dir or file underneith a dir . e.g. mypath.sub_get(name:"mysub_file.md",name_fix_find:true,name_fix:true)! . this will find Mysubfile.md as well as mysub_File.md and rename to mysub_file.md and open . params: .- name .

  • name_fix_find bool :means we will also find if name is same as the name_fix.
  • name_fix bool :if file found and name fix was different than file on filesystem, will rename .
  • dir_ensure bool :if dir_ensure on will fail if its not a dir .
  • file_ensure bool :if file_ensure on will fail if its not a dir .. will return SubGetError if error .

returns a path

fn (Path) template_write #

fn (mut path Path) template_write(template_ string, overwrite bool) !

fn (Path) write #

fn (mut path Path) write(content string) !

write content to the file, check is file if the path is a link to a file then will change the content of the file represented by the link

fn (Path) writeb #

fn (mut path Path) writeb(content []u8) !

write bytes to file

struct PathList #

struct PathList {
pub mut:
	// Root directory where listing started
	root string
	// Found paths
	paths []Path
}

Result of list operation

fn (PathList) copy #

fn (mut pathlist PathList) copy(dest string) !

Copy all paths to destination directory

fn (PathList) delete #

fn (mut pathlist PathList) delete() !

Delete all paths

struct RsyncArgs #

@[params]
struct RsyncArgs {
pub mut:
	source         string
	dest           string
	ipaddr_src     string // e.g. root@192.168.5.5:33 (can be without root@ or :port)
	ipaddr_dst     string
	delete         bool     // do we want to delete the destination
	ignore         []string // arguments to ignore e.g. ['*.pyc','*.bak']
	ignore_default bool = true // if set will ignore a common set
	debug          bool
	fast_rsync     bool
	sshkey         string
}

struct SubGetError #

struct SubGetError {
	Error
pub mut:
	msg        string
	path       string
	error_type JobErrorType
}

An internal struct for representing failed jobs.

fn (SubGetError) msg #

fn (err SubGetError) msg() string

fn (SubGetError) code #

fn (err SubGetError) code() int

struct SubGetParams #

@[params]
struct SubGetParams {
pub mut:
	name          string
	name_fix_find bool // means we will also find if name is same as the name_fix
	name_fix      bool // if file found and name fix was different than file on filesystem, will rename
	dir_ensure    bool // if dir_ensure on will fail if its not a dir
	file_ensure   bool // if file_ensure on will fail if its not a dir
}

struct TMPWriteArgs #

@[params]
struct TMPWriteArgs {
pub mut:
	name   string // optional name to remember it more easily
	tmpdir string
	text   string // text to put in file
	path   string // to overrule the path where script will be stored
	ext    string = 'sh'
}