From 3c6d7aa0dc29ac13d55b82ab1fe4457da66ab231 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Sun, 8 Sep 2024 14:01:22 -0700 Subject: [PATCH 1/9] Print Paths like constructor --- rhombus-lib/rhombus/private/amalgam/print.rkt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rhombus-lib/rhombus/private/amalgam/print.rkt b/rhombus-lib/rhombus/private/amalgam/print.rkt index 0611255c3..4ff819dad 100644 --- a/rhombus-lib/rhombus/private/amalgam/print.rkt +++ b/rhombus-lib/rhombus/private/amalgam/print.rkt @@ -147,7 +147,10 @@ [(display?) (display v op)] [else - (write v op)])] + (concat + (display "Path(" op) + (write (path->string v) op) + (display ")" op))])] [(and (procedure? v) (not (printer-ref v #f))) (define name (object-name v)) From 50b490aee7564c06a0a2f4acf504fa3759bfa411 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Thu, 8 Aug 2024 21:33:45 -0700 Subject: [PATCH 2/9] Add PathString annotation --- .../rhombus/private/amalgam/core-derived.rkt | 1 + .../rhombus/private/amalgam/path-object.rhm | 19 +++++++++++++++++++ .../rhombus/private/amalgam/path-object.rkt | 7 ++++++- .../rhombus/scribblings/reference/path.scrbl | 11 +++++++++-- rhombus/rhombus/tests/path.rhm | 8 ++++++++ 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 rhombus-lib/rhombus/private/amalgam/path-object.rhm diff --git a/rhombus-lib/rhombus/private/amalgam/core-derived.rkt b/rhombus-lib/rhombus/private/amalgam/core-derived.rkt index aac1421ee..572f755a9 100644 --- a/rhombus-lib/rhombus/private/amalgam/core-derived.rkt +++ b/rhombus-lib/rhombus/private/amalgam/core-derived.rkt @@ -6,6 +6,7 @@ "check.rhm" "maybe.rhm" "string.rhm" + "path-object.rhm" "when_unless.rhm" "where.rhm" "described_as.rhm" diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rhm b/rhombus-lib/rhombus/private/amalgam/path-object.rhm new file mode 100644 index 000000000..8f0584ae2 --- /dev/null +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rhm @@ -0,0 +1,19 @@ +#lang rhombus/private/amalgam/core + +import: + "core-meta.rkt" open + +use_static + +export: + PathString + +namespace PathString: + export: + to_path + + fun to_path(p :: PathString) :~ Path: + Path(p) + + annot.macro 'to_path': + 'converting(fun (p :: PathString) :~ Path: to_path(p))' diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rkt b/rhombus-lib/rhombus/private/amalgam/path-object.rkt index 6bd480552..41160ec6b 100644 --- a/rhombus-lib/rhombus/private/amalgam/path-object.rkt +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rkt @@ -6,6 +6,7 @@ "call-result-key.rkt" "define-arity.rkt" "compare-key.rkt" + (submod "annotation.rkt" for-class) (submod "string.rkt" static-infos) (submod "bytes.rkt" static-infos)) @@ -13,7 +14,9 @@ #f rhombus/bind rhombus/annot) - Path)) + Path) + (for-space rhombus/annot + PathString)) (module+ for-builtin (provide path-method-table)) @@ -71,3 +74,5 @@ #:primitive (path->string) #:static-infos ((#%call-result #,(get-string-static-infos))) (string->immutable-string (path->string s))) + +(define-annotation-syntax PathString (identifier-annotation path-string? ())) diff --git a/rhombus/rhombus/scribblings/reference/path.scrbl b/rhombus/rhombus/scribblings/reference/path.scrbl index 5c9a99179..0ae8ade52 100644 --- a/rhombus/rhombus/scribblings/reference/path.scrbl +++ b/rhombus/rhombus/scribblings/reference/path.scrbl @@ -19,13 +19,20 @@ Paths are @tech{comparable}, which means that generic operations like @doc( annot.macro 'Path' + annot.macro 'PathString' + annot.macro 'PathString.to_path' ){ - Matches a path value. + Matches a path value. The @rhombus(PathString, ~annot) annotation allows + @rhombus(ReadableString, ~annot) as well as @rhombus(Path, ~annot) values. + The @rhombus(PathString.to_path, ~annot) + @tech(~doc: guide_doc){converter annotation} allows + @rhombus(PathString, ~annot) values, but converts + @rhombus(ReadableString, ~annot) values to @rhombus(Path) values. } @doc( - fun Path(path :: Bytes || String || Path) :: Path + fun Path(path :: Bytes || ReadableString || Path) :: Path ){ Constructs a path given a byte string, string, or existing path. When a diff --git a/rhombus/rhombus/tests/path.rhm b/rhombus/rhombus/tests/path.rhm index 6cc40bce1..8a527c459 100644 --- a/rhombus/rhombus/tests/path.rhm +++ b/rhombus/rhombus/tests/path.rhm @@ -59,3 +59,11 @@ block: check p == q ~is #false check p != Path("/") ~is #false check p != q ~is #true + +block: + def s = "/etc" + def p1 = Path("/etc") + def p2 = (s :: PathString.to_path) + check p1 compares_equal p2 ~is #true + version_guard.at_least "8.13.0.1": + check p1 == p2 ~is #true From 490c78510f2c440f98a7badd228e971d19399ca1 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Sat, 7 Sep 2024 17:55:52 -0700 Subject: [PATCH 3/9] Add Path.parts --- .../rhombus/private/amalgam/path-object.rkt | 16 ++++++++++++---- .../rhombus/scribblings/reference/path.scrbl | 18 ++++++++++++++++++ rhombus/rhombus/tests/path.rhm | 8 ++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rkt b/rhombus-lib/rhombus/private/amalgam/path-object.rkt index 41160ec6b..6d423fca4 100644 --- a/rhombus-lib/rhombus/private/amalgam/path-object.rkt +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rkt @@ -6,9 +6,12 @@ "call-result-key.rkt" "define-arity.rkt" "compare-key.rkt" + "index-result-key.rkt" + "static-info.rkt" (submod "annotation.rkt" for-class) - (submod "string.rkt" static-infos) - (submod "bytes.rkt" static-infos)) + (submod "bytes.rkt" static-infos) + (submod "list.rkt" for-listable) + (submod "string.rkt" static-infos)) (provide (for-spaces (rhombus/namespace #f @@ -53,8 +56,8 @@ () #:methods (bytes - string - )) + parts + string)) (define/arity #:name Path (path c) #:static-infos ((#%call-result #,(get-path-static-infos))) @@ -70,6 +73,11 @@ #:static-infos ((#%call-result #,(get-bytes-static-infos))) (bytes->immutable-bytes (path->bytes s))) +(define/method (Path.parts p) + #:primitive (explode-path) + #:static-infos ((#%call-result #,(get-treelist-static-infos))) + (to-treelist #f (explode-path p))) + (define/method (Path.string s) #:primitive (path->string) #:static-infos ((#%call-result #,(get-string-static-infos))) diff --git a/rhombus/rhombus/scribblings/reference/path.scrbl b/rhombus/rhombus/scribblings/reference/path.scrbl index 0ae8ade52..71696d9fd 100644 --- a/rhombus/rhombus/scribblings/reference/path.scrbl +++ b/rhombus/rhombus/scribblings/reference/path.scrbl @@ -11,6 +11,7 @@ A @deftech{path} value represents a filesystem path. "path" Path path.bytes() + path.parts() path.string() ) @@ -76,6 +77,23 @@ Paths are @tech{comparable}, which means that generic operations like } +@doc( + fun Path.parts(path :: Path) :: List.of(Path || #'up || #'same) +){ + + Returns a list of path elements that constitute @rhombus(path). + + The @rhombus(Path.parts) function computes its result in time + proportional to the length of @rhombus(path). + +@examples( + def p = Path("/home/rhombus/shape.txt") + Path.parts(p) + p.parts() +) + +} + @doc( fun Path.string(path :: Path) :: String ){ diff --git a/rhombus/rhombus/tests/path.rhm b/rhombus/rhombus/tests/path.rhm index 8a527c459..bc45155d1 100644 --- a/rhombus/rhombus/tests/path.rhm +++ b/rhombus/rhombus/tests/path.rhm @@ -67,3 +67,11 @@ block: check p1 compares_equal p2 ~is #true version_guard.at_least "8.13.0.1": check p1 == p2 ~is #true + +block: + def p1 = Path("/etc/passwd") + check p1.parts() ~is [Path("/"), Path("etc"), Path("passwd")] + def p2 = Path("C:/windows") + check p2.parts() ~is [Path("C:"), Path("windows")] + def p3 = Path("../a/b/./c") + check p3.parts() ~is [#'up, Path("a"), Path("b"), #'same, Path("c")] From 63e093d9f2d95f118bbcd84e9a6759cf7ed1dfb5 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Sun, 8 Sep 2024 14:49:44 -0700 Subject: [PATCH 4/9] Add Path.extend --- .../rhombus/private/amalgam/path-object.rkt | 10 ++++- .../scribblings/reference/appendable.scrbl | 1 + .../rhombus/scribblings/reference/path.scrbl | 41 +++++++++++++++++++ rhombus/rhombus/tests/path.rhm | 6 +++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rkt b/rhombus-lib/rhombus/private/amalgam/path-object.rkt index 6d423fca4..4bc26ddad 100644 --- a/rhombus-lib/rhombus/private/amalgam/path-object.rkt +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rkt @@ -5,6 +5,7 @@ "realm.rkt" "call-result-key.rkt" "define-arity.rkt" + "append-key.rkt" "compare-key.rkt" "index-result-key.rkt" "static-info.rkt" @@ -42,7 +43,8 @@ (define-primitive-class Path path #:lift-declaration #:no-constructor-static-info - #:instance-static-info ((#%compare ((< path path>?) (>= path>=?) @@ -56,6 +58,7 @@ () #:methods (bytes + extend parts string)) @@ -73,6 +76,11 @@ #:static-infos ((#%call-result #,(get-bytes-static-infos))) (bytes->immutable-bytes (path->bytes s))) +(define/method (Path.extend p . ss) + #:primitive (build-path) + #:static-infos ((#%call-result #,(get-path-static-infos))) + (apply build-path p ss)) + (define/method (Path.parts p) #:primitive (explode-path) #:static-infos ((#%call-result #,(get-treelist-static-infos))) diff --git a/rhombus/rhombus/scribblings/reference/appendable.scrbl b/rhombus/rhombus/scribblings/reference/appendable.scrbl index 91049d850..854866831 100644 --- a/rhombus/rhombus/scribblings/reference/appendable.scrbl +++ b/rhombus/rhombus/scribblings/reference/appendable.scrbl @@ -29,6 +29,7 @@ An @deftech{appendable} value is one that supports @rhombus(++). Maps, operator ((v1 :: ReadableString) ++ (v2 :: ReadableString)) :: String operator ((v1 :: Bytes) ++ (v2 :: Bytes)) :: MutableBytes + operator ((v1 :: Path) ++ (v2 :: PathString)) :: Path operator ((v1 :: Appendable) ++ (v2 :: Appendable)) :: Any ){ diff --git a/rhombus/rhombus/scribblings/reference/path.scrbl b/rhombus/rhombus/scribblings/reference/path.scrbl index 71696d9fd..8e54be3d4 100644 --- a/rhombus/rhombus/scribblings/reference/path.scrbl +++ b/rhombus/rhombus/scribblings/reference/path.scrbl @@ -11,6 +11,7 @@ A @deftech{path} value represents a filesystem path. "path" Path path.bytes() + path.extend(part, ...) path.parts() path.string() ) @@ -77,6 +78,46 @@ Paths are @tech{comparable}, which means that generic operations like } +@doc( + fun Path.extend(path :: Path, + part :: PathString | #'up | #'same, ...) :: Path +){ + + Creates a path given a base path and any number of sub-path + extensions. See also @rhombus(++). If @rhombus(path) is an absolute path, + the result is an absolute path, otherwise the result is a relative path. + + The @rhombus(path) and each @rhombus(part) must be either a relative + path, the symbol @rhombus(#'up) (indicating the relative parent + directory), or the symbol @rhombus(#'same) (indicating the + relative current directory). For Windows paths, if @rhombus(path) is a + drive specification (with or without a trailing slash) the first + @rhombus(part) can be an absolute (driveless) path. For all platforms, + the last @rhombus(part) can be a filename. + + The @rhombus(path) and @rhombus(part) arguments can be paths for + any platform. The platform for the resulting path is inferred from the + @rhombus(path) and @rhombus(part) arguments, where string arguments imply + a path for the current platform. If different arguments are for + different platforms, the @rhombus(Exn.Fail.Contract, ~class) exception + is thrown. If no argument implies a platform (i.e., all are @rhombus(#'up) + or @rhombus(#'same)), the generated path is for the current platform. + + Each @rhombus(part) and @rhombus(path) can optionally end in a directory + separator. If the last @rhombus(part) ends in a separator, it is + included in the resulting path. + + The @rhombus(build-path) procedure builds a path @italic{without} + checking the validity of the path or accessing the filesystem. + +@examples( + def p = Path("/home/rhombus") + Path.extend(p, "shape.txt") + p.extend("shape.txt") + p ++ "shape.txt" +) + +} @doc( fun Path.parts(path :: Path) :: List.of(Path || #'up || #'same) ){ diff --git a/rhombus/rhombus/tests/path.rhm b/rhombus/rhombus/tests/path.rhm index bc45155d1..78801a0bc 100644 --- a/rhombus/rhombus/tests/path.rhm +++ b/rhombus/rhombus/tests/path.rhm @@ -75,3 +75,9 @@ block: check p2.parts() ~is [Path("C:"), Path("windows")] def p3 = Path("../a/b/./c") check p3.parts() ~is [#'up, Path("a"), Path("b"), #'same, Path("c")] + +block: + check Path("/").extend("etc", Path("passwd")) ~is Path("/etc/passwd") + check Path.extend("C:", "win32", "sys") ~is Path("C:/win32/sys") + check Path("/") ++ Path("etc") ++ "passwd" ~is Path("/etc/passwd") + check Path("..") ++ "a" ++ "b" ++ #'same ++ "c" ~is Path("../a/b/./c") From 6ea9402b04f40214cc68510105b581427b56434b Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Mon, 9 Sep 2024 14:20:16 -0700 Subject: [PATCH 5/9] Add `Path.is_absolute` --- rhombus-lib/rhombus/private/amalgam/path-object.rkt | 5 +++++ rhombus/rhombus/scribblings/reference/path.scrbl | 8 ++++++++ rhombus/rhombus/tests/path.rhm | 10 ++++++++++ 3 files changed, 23 insertions(+) diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rkt b/rhombus-lib/rhombus/private/amalgam/path-object.rkt index 4bc26ddad..46bc5bbed 100644 --- a/rhombus-lib/rhombus/private/amalgam/path-object.rkt +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rkt @@ -59,6 +59,7 @@ #:methods (bytes extend + is_absolute parts string)) @@ -81,6 +82,10 @@ #:static-infos ((#%call-result #,(get-path-static-infos))) (apply build-path p ss)) +(define/method (Path.is_absolute p) + #:primitive (absolute-path?) + (absolute-path? p)) + (define/method (Path.parts p) #:primitive (explode-path) #:static-infos ((#%call-result #,(get-treelist-static-infos))) diff --git a/rhombus/rhombus/scribblings/reference/path.scrbl b/rhombus/rhombus/scribblings/reference/path.scrbl index 8e54be3d4..75343fba4 100644 --- a/rhombus/rhombus/scribblings/reference/path.scrbl +++ b/rhombus/rhombus/scribblings/reference/path.scrbl @@ -12,6 +12,7 @@ A @deftech{path} value represents a filesystem path. Path path.bytes() path.extend(part, ...) + path.is_absolute() path.parts() path.string() ) @@ -118,6 +119,13 @@ Paths are @tech{comparable}, which means that generic operations like ) } + +@doc(fun Path.is_absolute(path :: Path)){ + + Returns @rhombus(#true) if @rhombus(path) is an absolute path, @rhombus(#false) + otherwise. This procedure does not access the filesystem. +} + @doc( fun Path.parts(path :: Path) :: List.of(Path || #'up || #'same) ){ diff --git a/rhombus/rhombus/tests/path.rhm b/rhombus/rhombus/tests/path.rhm index 78801a0bc..6fab86596 100644 --- a/rhombus/rhombus/tests/path.rhm +++ b/rhombus/rhombus/tests/path.rhm @@ -81,3 +81,13 @@ block: check Path.extend("C:", "win32", "sys") ~is Path("C:/win32/sys") check Path("/") ++ Path("etc") ++ "passwd" ~is Path("/etc/passwd") check Path("..") ++ "a" ++ "b" ++ #'same ++ "c" ~is Path("../a/b/./c") + +block: + def p1 = Path("/etc") + def p2 = Path("etc") + check Path.is_absolute(p1) ~is #true + check Path.is_absolute("/etc") ~is #true + check p1.is_absolute() ~is #true + check Path.is_absolute(p2) ~is #false + check Path.is_absolute("etc") ~is #false + check p2.is_absolute() ~is #false From ce9e26377e5117c8be0129e47cf701522504a7ad Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Sun, 15 Sep 2024 16:16:51 -0700 Subject: [PATCH 6/9] Path.current_directory impl --- rhombus-lib/rhombus/private/amalgam/path-object.rkt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rkt b/rhombus-lib/rhombus/private/amalgam/path-object.rkt index 46bc5bbed..d9887e669 100644 --- a/rhombus-lib/rhombus/private/amalgam/path-object.rkt +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rkt @@ -11,6 +11,7 @@ "static-info.rkt" (submod "annotation.rkt" for-class) (submod "bytes.rkt" static-infos) + (submod "function.rkt" for-info) (submod "list.rkt" for-listable) (submod "string.rkt" static-infos)) @@ -54,6 +55,8 @@ #:translucent #:fields ([bytes Path.bytes #,(get-bytes-static-infos)]) + #:namespace-fields + ([current_directory current-directory]) #:properties () #:methods @@ -72,6 +75,11 @@ [else (raise-argument-error* who rhombus-realm "String || Bytes || Path" c)])) +(define-static-info-syntax current-directory + (#%function-arity 3) + (#%call-result #,(get-path-static-infos)) + . #,(get-function-static-infos)) + (define/method (Path.bytes s) #:primitive (path->bytes) #:static-infos ((#%call-result #,(get-bytes-static-infos))) From 2b6c184a71142530da7ed9c23a66d106c69255cb Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Mon, 9 Sep 2024 18:16:06 -0700 Subject: [PATCH 7/9] Path.to_complete_path implementation --- .../rhombus/private/amalgam/path-object.rhm | 14 ++++++++++++++ .../rhombus/private/amalgam/path-object.rkt | 8 +++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rhm b/rhombus-lib/rhombus/private/amalgam/path-object.rhm index 8f0584ae2..b503caa08 100644 --- a/rhombus-lib/rhombus/private/amalgam/path-object.rhm +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rhm @@ -11,9 +11,23 @@ export: namespace PathString: export: to_path + to_complete_path fun to_path(p :: PathString) :~ Path: Path(p) + fun + | to_complete_path(p :: PathString): + Path.to_complete_path(p) + | to_complete_path(p :: PathString, ~relative_to: base_path :: PathString): + Path.to_complete_path(p, ~relative_to: base_path) + annot.macro 'to_path': 'converting(fun (p :: PathString) :~ Path: to_path(p))' + + annot.macro + | 'to_complete_path(~relative_to: $base_path)': + 'converting(fun (p :: PathString) :~ Path: + to_complete_path(p, ~relative_to: $base_path))' + | 'to_complete_path': + 'converting(fun (p :: PathString) :~ Path: to_complete_path(p))' diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rkt b/rhombus-lib/rhombus/private/amalgam/path-object.rkt index d9887e669..0b86c6316 100644 --- a/rhombus-lib/rhombus/private/amalgam/path-object.rkt +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rkt @@ -64,7 +64,8 @@ extend is_absolute parts - string)) + string + to_complete_path)) (define/arity #:name Path (path c) #:static-infos ((#%call-result #,(get-path-static-infos))) @@ -104,4 +105,9 @@ #:static-infos ((#%call-result #,(get-string-static-infos))) (string->immutable-string (path->string s))) +(define/method (Path.to_complete_path p #:relative_to [base-path (current-directory)]) + #:primitive (path->complete-path) + #:static-infos ((#%call-result #,(get-path-static-infos))) + (path->complete-path p base-path)) + (define-annotation-syntax PathString (identifier-annotation path-string? ())) From 45663e12dd349e22403c1c1e5e13231ba599ee91 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Mon, 7 Oct 2024 17:43:14 -0700 Subject: [PATCH 8/9] Add Port.Input.open_file Port.Output.open_file --- rhombus-lib/rhombus/private/amalgam/port.rkt | 23 ++++++- .../rhombus/scribblings/reference/port.scrbl | 69 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/rhombus-lib/rhombus/private/amalgam/port.rkt b/rhombus-lib/rhombus/private/amalgam/port.rkt index 1f11ae05a..2c86e2046 100644 --- a/rhombus-lib/rhombus/private/amalgam/port.rkt +++ b/rhombus-lib/rhombus/private/amalgam/port.rkt @@ -71,7 +71,8 @@ [open_bytes Port.Output.open_bytes] [open_string Port.Output.open_string] [get_bytes Port.Output.get_bytes] - [get_string Port.Output.get_string]) + [get_string Port.Output.get_string] + ExistsFlag) #:properties () #:methods ([flush Port.Output.flush] @@ -124,6 +125,16 @@ any [any_one any-one]) +(define-simple-symbol-enum ExistsFlag + error + replace + truncate + [must_truncate must-truncate] + [truncate_replace truncate/replace] + update + [can_update can-update] + append) + (define (check-input-port who ip) (unless (input-port? ip) (raise-argument-error* who rhombus-realm "Port.Input" ip))) @@ -139,6 +150,11 @@ [(bstr) (open-input-bytes bstr)] [(bstr name) (open-input-bytes bstr name)])) +(define/arity (Port.Input.open_file path) + #:primitive (open-input-file) + #:static-infos ((#%call-result #,(get-input-port-static-infos))) + (open-input-file path)) + (define/arity Port.Input.open_string #:primitive (open-input-string) #:static-infos ((#%call-result #,(get-input-port-static-infos))) @@ -153,6 +169,11 @@ [() (open-output-bytes)] [(name) (open-output-bytes name)])) +(define/arity (Port.Output.open_file path #:exists [exists 'error]) + #:primitive (open-output-file) + #:static-infos ((#%call-result #,(get-output-port-static-infos))) + (open-output-file path #:exists (->ExistsFlag exists))) + (define/arity Port.Output.open_string #:primitive (open-output-string) #:static-infos ((#%call-result #,(get-output-string-port-static-infos))) diff --git a/rhombus/rhombus/scribblings/reference/port.scrbl b/rhombus/rhombus/scribblings/reference/port.scrbl index f38c387dc..5c53a5285 100644 --- a/rhombus/rhombus/scribblings/reference/port.scrbl +++ b/rhombus/rhombus/scribblings/reference/port.scrbl @@ -112,6 +112,14 @@ output. Moreover, an @deftech{input string port} or an } +@doc( + fun Port.Input.open_file(file :: PathString) :: Port.Input +){ + + Creates an @tech{input port} that reads from the @tech{path} @rhombus(file). + +} + @doc( fun Port.Input.open_string(str :: ReadableString, name :: Symbol = #'string) @@ -314,6 +322,51 @@ output. Moreover, an @deftech{input string port} or an } +@doc( + fun Port.Output.open_file(path :: PathString, + ~exists: exists_flag + :: Port.Output.ExistsFlag = #'error) + :: Port.Output +){ + + Creates an @tech{output port} that writes to the @tech{path} @rhombus(file). + The @rhombus(exists_flag) argument specifies how to handle/require + files that already exist: + + @itemlist( + + @item{@rhombus(#'error) --- throws @rhombus(Exn.Fail.Filesystem.Exists) + if the file exists.} + + @item{@rhombus(#'replace) --- remove the old file, if it + exists, and write a new one.} + + @item{@rhombus(#'truncate) --- remove all old data, if the file + exists.} + + @item{@rhombus(#'must_truncate) --- remove all old data in an + existing file; if the file does not exist, the + @rhombus(Exn.Fail.Filesystem) exception is thrown.} + + @item{@rhombus(#'truncate_replace) --- try @rhombus(#'truncate); + if it fails (perhaps due to file permissions), try + @rhombus(#'replace).} + + @item{@rhombus(#'update) --- open an existing file without + truncating it; if the file does not exist, the + @rhombus(Exn.Fail.Filesystem) exception is thrown.} + + @item{@rhombus(#'can_update) --- open an existing file without + truncating it, or create the file if it does not exist.} + + @item{@rhombus(#'append) --- append to the end of the file, + whether it already exists or not; on Windows, + @rhombus(#'append) is equivalent to @rhombus(#'update), except that + the file is not required to exist, and the file position is + immediately set to the end of the file after opening it.} + +)} + @doc( fun Port.Output.open_bytes(name :: Symbol = #'string) :: Port.Output.String @@ -357,3 +410,19 @@ output. Moreover, an @deftech{input string port} or an Flushes the content of @rhombus(out)'s buffer. } + +@doc( + enum Port.Output.ExistsFlag: + error + append + update + can_update + replace + truncate + must_truncate + truncate_replace +){ + + Flags for handling existing files when opening @tech{output ports}. + +} From e999cefb769229de8af138200933b673121061ea Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Fri, 11 Oct 2024 16:20:32 -0700 Subject: [PATCH 9/9] Add Path.read_with Path.write_with --- .../rhombus/private/amalgam/path-object.rkt | 12 ++++++++- .../rhombus/scribblings/reference/path.scrbl | 25 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/rhombus-lib/rhombus/private/amalgam/path-object.rkt b/rhombus-lib/rhombus/private/amalgam/path-object.rkt index 0b86c6316..ba4241336 100644 --- a/rhombus-lib/rhombus/private/amalgam/path-object.rkt +++ b/rhombus-lib/rhombus/private/amalgam/path-object.rkt @@ -64,8 +64,10 @@ extend is_absolute parts + read_with string - to_complete_path)) + to_complete_path + write_with)) (define/arity #:name Path (path c) #:static-infos ((#%call-result #,(get-path-static-infos))) @@ -100,6 +102,10 @@ #:static-infos ((#%call-result #,(get-treelist-static-infos))) (to-treelist #f (explode-path p))) +(define/method (Path.read_with p f) + #:primitive (call-with-input-file) + (call-with-input-file p f)) + (define/method (Path.string s) #:primitive (path->string) #:static-infos ((#%call-result #,(get-string-static-infos))) @@ -110,4 +116,8 @@ #:static-infos ((#%call-result #,(get-path-static-infos))) (path->complete-path p base-path)) +(define/method (Path.write_with p f #:exists [exists 'error]) + #:primitive (call-with-output-file) + (call-with-output-file p f #:exists exists)) + (define-annotation-syntax PathString (identifier-annotation path-string? ())) diff --git a/rhombus/rhombus/scribblings/reference/path.scrbl b/rhombus/rhombus/scribblings/reference/path.scrbl index 75343fba4..2934248de 100644 --- a/rhombus/rhombus/scribblings/reference/path.scrbl +++ b/rhombus/rhombus/scribblings/reference/path.scrbl @@ -14,7 +14,9 @@ A @deftech{path} value represents a filesystem path. path.extend(part, ...) path.is_absolute() path.parts() + path.read_with(proc) path.string() + path.write_with(proc, ...) ) Paths are @tech{comparable}, which means that generic operations like @@ -143,6 +145,29 @@ Paths are @tech{comparable}, which means that generic operations like } +@doc( + fun Path.read_with(path :: Path, read_proc :: Function.of_arity(1)) +){ + + Opens @rhombus(path) for reading and calls @rhombus(read_proc) with the + @tech{input port}. The result of @rhombus(read_proc) is the result of + @rhombus(Path.read_with). + +} + +@doc( + fun Path.write_with(path :: Path, + proc :: Function.of_arity(1), + ~exists: exists_flag + :: Port.Output.ExistsFlag = #'error) +){ + + Opens @rhombus(path) for writing and calls @rhombus(write_proc) with the + @tech{output port}. The result of @rhombus(write_proc) is the result of + @rhombus(Path.write_with). + +} + @doc( fun Path.string(path :: Path) :: String ){