~sircmpwn/hare-users

1

Suggested changes to stdlib::path API

Details
Message ID
<c968f23f-0983-3d55-59c2-f0cde51691aa@posteo.net>
DKIM signature
missing
Download raw message
I would like to suggest some changes to the stdlib::path API that would
make it cleaner to use. Currently it feels clunky and disjointed, and
common operations require many variable assignments or workarounds using
stdlib::strings.

The conceptual model of these functions treats a path::buffer as a stack
of path segments, and the file extension(s) of the final path segment as
a mini-stack too. In the case of path segments, this reflects filetree
traversal (i.e. pop() <=> cd ..). In the case of file extensions, it
just makes it simpler to manipulate them.

This stack model is already hinted at in the current API with functions
like add() and extension(), but I think explicitly committing to it is a
good idea.

Other changes include:
- removing functions that do allocs.
- making arguments variadic where sensible
- making most functions only take a *buffer not a (*buffer|str), for
consistency and simplicity of implementation. I'm not too bothered about
this point, though.

Below is the set of functions that I think should replace the current
API, annotated with additional changes and context.

m modified or renamed
+ added
- removed

Basic
=====
m init(str...) (buffer | errors::overflow)
  reset(*buffer) void
m abs(*buffer) bool
  string(*buffer) str

- allocate(*buffer) str [use strings::dup]
- join(str...) str [use path::init and strings::dup]
- set(*buffer, str...) [use path::init]

It makes more sense if the whole module uses no allocs, and the strings
module is used in the path module anyway already. set() could be kept if
stack space is a significant concern.

Path segment level
==================
m push(*buffer, str...) (str | errors::overflow)
+ pop(*buffer) (str | void)
m peek(*buffer) (str | void) [replaces old 'basename']
m dirname(*buffer) (str | void)

pop and peek should return void only when the path is empty or is the
root dir.

File extension level
====================
+ push_ext(*buffer, str...) (str | errors::overflow)
m pop_ext(*buffer) (str | void) [similar to old extension()]
+ peek_ext(*buffer) (str | void)
+ basename(*buffer) str [returns filename up to first '.']

the *_ext functions should not use a leading '.' before the extension.
This is so that filenames ending in '.' have extension "", and filenames
with no extension have extension `void`. This also makes it easier to
manipulate extensions using strings::{split, join}.

Iterators
=========
m iter(*buffer) iterator
+ riter(*buffer) iterator [reverse iter is just as common as forward]
  next(*iterator) (str|void)
+ remaining(*iterator) str

Iterators should be (documented as) copyable to save their state
Details
Message ID
<CS0MQX2BV68E.1DV4H893ZGMZC@taiga>
In-Reply-To
<c968f23f-0983-3d55-59c2-f0cde51691aa@posteo.net> (view parent)
DKIM signature
missing
Download raw message
+1 to at least some of this, want to see the work broken up into
smallish patches if at all possible.

On Fri Jan 13, 2023 at 6:54 PM CET, Autumn! wrote:
> - removing functions that do allocs.
> - making arguments variadic where sensible

+1

> - making most functions only take a *buffer not a (*buffer|str), for
> consistency and simplicity of implementation. I'm not too bothered about
> this point, though.

+1

> Below is the set of functions that I think should replace the current
> API, annotated with additional changes and context.
>
> m modified or renamed
> + added
> - removed
>
> Basic
> =====
> m init(str...) (buffer | errors::overflow)
>   reset(*buffer) void
> m abs(*buffer) bool
>   string(*buffer) str
>
> - allocate(*buffer) str [use strings::dup]
> - join(str...) str [use path::init and strings::dup]
> - set(*buffer, str...) [use path::init]
>
> It makes more sense if the whole module uses no allocs, and the strings
> module is used in the path module anyway already. set() could be kept if
> stack space is a significant concern.

I think set makes sense on a semantic level

> Path segment level
> ==================
> m push(*buffer, str...) (str | errors::overflow)
> + pop(*buffer) (str | void)
> m peek(*buffer) (str | void) [replaces old 'basename']
> m dirname(*buffer) (str | void)
>
> pop and peek should return void only when the path is empty or is the
> root dir.

I think we should add/keep basename, at least as an alias for peek,
because it uses the common semantic name for this operation that users
will understand.

> File extension level
> ====================
> + push_ext(*buffer, str...) (str | errors::overflow)
> m pop_ext(*buffer) (str | void) [similar to old extension()]
> + peek_ext(*buffer) (str | void)
> + basename(*buffer) str [returns filename up to first '.']
>
> the *_ext functions should not use a leading '.' before the extension.
> This is so that filenames ending in '.' have extension "", and filenames
> with no extension have extension `void`. This also makes it easier to
> manipulate extensions using strings::{split, join}.

Not sold on the extension changes proposed here. File extensions are
kind of weird and I would prefer to keep heuristics associated with them
to a reasonably sane minimum. Could be convinced otherwise.

> Iterators
> =========
> m iter(*buffer) iterator
> + riter(*buffer) iterator [reverse iter is just as common as forward]
>   next(*iterator) (str|void)
> + remaining(*iterator) str
>
> Iterators should be (documented as) copyable to save their state

+1
Reply to thread Export thread (mbox)