From 5b60de3c403face4df8f3b881be62a86e0e18149 Mon Sep 17 00:00:00 2001 From: juerg Date: Thu, 11 Jan 2024 22:42:00 +0100 Subject: [PATCH] added file link operations --- .../cheatsheet/section/IoFileSection.java | 43 ++- .../venice/impl/functions/IOFunctions.java | 281 +++++++++++++++++- 2 files changed, 299 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/IoFileSection.java b/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/IoFileSection.java index 4ede71eee..85f1369e9 100644 --- a/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/IoFileSection.java +++ b/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/IoFileSection.java @@ -53,60 +53,75 @@ public DocSection section() { file.addItem(diBuilder.getDocItem("io/file-size", false)); file.addItem(diBuilder.getDocItem("io/file-last-modified", false)); - final DocSection file_dir = new DocSection("file dir", "io.filedir"); + final DocSection file_dir = new DocSection("dir", "io.filedir"); all.addSection(file_dir); file_dir.addItem(diBuilder.getDocItem("io/mkdir", false)); file_dir.addItem(diBuilder.getDocItem("io/mkdirs", false)); - final DocSection file_io = new DocSection("file i/o", "io.fileio"); + final DocSection file_io = new DocSection("slurp/spit", "io.fileio"); all.addSection(file_io); file_io.addItem(diBuilder.getDocItem("io/slurp")); file_io.addItem(diBuilder.getDocItem("io/slurp-lines")); file_io.addItem(diBuilder.getDocItem("io/spit")); - final DocSection file_list = new DocSection("file list", "io.filelist"); + final DocSection file_list = new DocSection("list", "io.filelist"); all.addSection(file_list); file_list.addItem(diBuilder.getDocItem("io/list-files", false)); file_list.addItem(diBuilder.getDocItem("io/list-files-glob", false)); file_list.addItem(diBuilder.getDocItem("io/list-file-tree", false)); file_list.addItem(diBuilder.getDocItem("io/list-file-tree-lazy", false)); - final DocSection file_del = new DocSection("file delete", "io.filedelete"); + final DocSection file_del = new DocSection("delete", "io.filedelete"); all.addSection(file_del); file_del.addItem(diBuilder.getDocItem("io/delete-file", false)); file_del.addItem(diBuilder.getDocItem("io/delete-files-glob", false)); file_del.addItem(diBuilder.getDocItem("io/delete-file-tree", false)); file_del.addItem(diBuilder.getDocItem("io/delete-file-on-exit", false)); - final DocSection file_cpy = new DocSection("file copy", "io.filecopy"); + final DocSection file_cpy = new DocSection("copy", "io.filecopy"); all.addSection(file_cpy); file_cpy.addItem(diBuilder.getDocItem("io/copy-file", false)); file_cpy.addItem(diBuilder.getDocItem("io/copy-files-glob", false)); file_cpy.addItem(diBuilder.getDocItem("io/copy-file-tree", false)); - final DocSection file_mov = new DocSection("file move", "io.filemove"); + final DocSection file_mov = new DocSection("move", "io.filemove"); all.addSection(file_mov); file_mov.addItem(diBuilder.getDocItem("io/move-file", false)); file_mov.addItem(diBuilder.getDocItem("io/move-files-glob", false)); - final DocSection file_touch = new DocSection("file touch", "io.filetouch"); + final DocSection file_touch = new DocSection("touch", "io.filetouch"); all.addSection(file_touch); file_touch.addItem(diBuilder.getDocItem("io/touch-file")); - final DocSection file_test = new DocSection("file test", "io.filetest"); + final DocSection file_perm = new DocSection("permissions", "io.filepermissions"); + all.addSection(file_perm); + file_perm.addItem(diBuilder.getDocItem("io/file-can-read?", false)); + file_perm.addItem(diBuilder.getDocItem("io/file-can-write?", false)); + file_perm.addItem(diBuilder.getDocItem("io/file-can-execute?", false)); + file_perm.addItem(diBuilder.getDocItem("io/file-set-readable", false)); + file_perm.addItem(diBuilder.getDocItem("io/file-set-writable", false)); + file_perm.addItem(diBuilder.getDocItem("io/file-set-executable", false)); + + final DocSection file_link = new DocSection("links", "io.filelinks"); + all.addSection(file_link); + file_link.addItem(diBuilder.getDocItem("io/file-symbolic-link?", false)); + file_link.addItem(diBuilder.getDocItem("io/file-create-symbolic-link", false)); + file_link.addItem(diBuilder.getDocItem("io/file-create-hard-link", false)); + + final DocSection file_test = new DocSection("test", "io.filetest"); all.addSection(file_test); file_test.addItem(diBuilder.getDocItem("io/file?")); file_test.addItem(diBuilder.getDocItem("io/file-absolute?")); file_test.addItem(diBuilder.getDocItem("io/exists-file?")); file_test.addItem(diBuilder.getDocItem("io/exists-dir?")); - file_test.addItem(diBuilder.getDocItem("io/file-can-read?", false)); - file_test.addItem(diBuilder.getDocItem("io/file-can-write?", false)); - file_test.addItem(diBuilder.getDocItem("io/file-can-execute?", false)); + file_perm.addItem(diBuilder.getDocItem("io/file-can-read?", false)); + file_perm.addItem(diBuilder.getDocItem("io/file-can-write?", false)); + file_perm.addItem(diBuilder.getDocItem("io/file-can-execute?", false)); file_test.addItem(diBuilder.getDocItem("io/file-hidden?", false)); file_test.addItem(diBuilder.getDocItem("io/file-symbolic-link?", false)); file_test.addItem(diBuilder.getDocItem("io/file-within-dir?")); - final DocSection file_glob = new DocSection("file glob", "io.fileglob"); + final DocSection file_glob = new DocSection("glob", "io.fileglob"); all.addSection(file_glob); file_glob.addItem(diBuilder.getDocItem("io/glob-path-matcher", false)); file_glob.addItem(diBuilder.getDocItem("io/file-matches-glob?")); @@ -131,13 +146,13 @@ public DocSection section() { file_watch.addItem(diBuilder.getDocItem("io/watch-dir", false)); file_watch.addItem(diBuilder.getDocItem("io/close-watcher", false)); - final DocSection file_tmp = new DocSection("file tmp", "io.filetmp"); + final DocSection file_tmp = new DocSection("temporary", "io.filetmp"); all.addSection(file_tmp); file_tmp.addItem(diBuilder.getDocItem("io/temp-file")); file_tmp.addItem(diBuilder.getDocItem("io/temp-dir")); file_tmp.addItem(diBuilder.getDocItem("io/tmp-dir")); - final DocSection file_other = new DocSection("file user", "io.fileuser"); + final DocSection file_other = new DocSection("user dir", "io.fileuser"); all.addSection(file_other); file_other.addItem(diBuilder.getDocItem("io/user-dir")); file_other.addItem(diBuilder.getDocItem("io/user-home-dir")); diff --git a/src/main/java/com/github/jlangch/venice/impl/functions/IOFunctions.java b/src/main/java/com/github/jlangch/venice/impl/functions/IOFunctions.java index abaef7b14..0131e9bea 100644 --- a/src/main/java/com/github/jlangch/venice/impl/functions/IOFunctions.java +++ b/src/main/java/com/github/jlangch/venice/impl/functions/IOFunctions.java @@ -589,7 +589,11 @@ public VncVal apply(final VncList args) { .examples( "(io/file-can-read? \"/tmp/test.txt\")") .seeAlso( - "io/file-can-write?", "io/file-can-execute?", "io/file-hidden?", "io/file-symbolic-link?") + "io/file-set-readable", + "io/file-can-write?", + "io/file-can-execute?", + "io/file-hidden?", + "io/file-symbolic-link?") .build() ) { @Override @@ -618,7 +622,11 @@ public VncVal apply(final VncList args) { .examples( "(io/file-can-write? \"/tmp/test.txt\")") .seeAlso( - "io/file-can-read?", "io/file-can-execute?", "io/file-hidden?", "io/file-symbolic-link?") + "io/file-set-writable", + "io/file-can-read?", + "io/file-can-execute?", + "io/file-hidden?", + "io/file-symbolic-link?") .build() ) { @Override @@ -647,7 +655,11 @@ public VncVal apply(final VncList args) { .examples( "(io/file-can-execute? \"/tmp/test.txt\")") .seeAlso( - "io/file-can-read?", "io/file-can-write?", "io/file-hidden?", "io/file-symbolic-link?") + "io/file-set-executable", + "io/file-can-read?", + "io/file-can-write?", + "io/file-hidden?", + "io/file-symbolic-link?") .build() ) { @Override @@ -664,6 +676,154 @@ public VncVal apply(final VncList args) { private static final long serialVersionUID = -1848883965231344442L; }; + public static VncFunction io_file_set_readable = + new VncFunction( + "io/file-set-readable", + VncFunction + .meta() + .arglists("(io/file-set-readable f readable owner-only)") + .doc( + "Set the owner’s read permission to the file or directory f. " + + "f must be a file or a string (file path)." + + "\n\n" + + "Returns true if and only if the operation succeeded. The " + + "operation will fail if the user does not have permission to " + + "change the access permissions of this abstract pathname. If " + + "'readable' is false and the underlying file system does not " + + "implement a read permission, then the operation will fail." + + "\n\n" + + "If 'readable' is true sets the access permission to allow read " + + "operations; if false to disallow read operations. " + + "\n\n" + + "If 'owner-only' is true the read permission applies only to the " + + "owner's read permission; otherwise, it applies to everybody. If " + + "the underlying file system can not distinguish the owner's read " + + "permission from that of others, then the permission will apply to " + + "everybody, regardless of this value.") + .examples( + "(io/file-set-readable \"/tmp/test.txt\" true true)") + .seeAlso( + "io/file-can-read?", + "io/file-set-writable", + "io/file-set-executable") + .build() + ) { + @Override + public VncVal apply(final VncList args) { + ArityExceptions.assertArity(this, args, 3); + + final File f = convertToFile( + args.first(), + "Function 'io/file-set-readable' does not allow %s as f"); + + final boolean on = Coerce.toVncBoolean(args.second()).getValue(); + final boolean ownerOnly = Coerce.toVncBoolean(args.third()).getValue(); + + return VncBoolean.of(f.setReadable(on, ownerOnly)); + } + + private static final long serialVersionUID = -1848883965231344442L; + }; + + public static VncFunction io_file_set_writable = + new VncFunction( + "io/file-set-writable", + VncFunction + .meta() + .arglists("(io/file-set-writable f writable owner-only)") + .doc( + "Set the owner’s write permission to the file or directory f. " + + "f must be a file or a string (file path)." + + "\n\n" + + "Returns true if and only if the operation succeeded. The " + + "operation will fail if the user does not have permission to " + + "change the access permissions of this abstract pathname. If " + + "'writable' is false and the underlying file system does not " + + "implement a read permission, then the operation will fail." + + "\n\n" + + "If 'writable' is true sets the access permission to allow write " + + "operations; if false to disallow write operations. " + + "\n\n" + + "If 'owner-only' is true the write permission applies only to the " + + "owner's write permission; otherwise, it applies to everybody. If " + + "the underlying file system can not distinguish the owner's write " + + "permission from that of others, then the permission will apply to " + + "everybody, regardless of this value.") + .examples( + "(io/file-set-writable \"/tmp/test.txt\" true true)") + .seeAlso( + "io/file-can-write?", + "io/file-set-readable", + "io/file-set-executable") + .build() + ) { + @Override + public VncVal apply(final VncList args) { + ArityExceptions.assertArity(this, args, 3); + + final File f = convertToFile( + args.first(), + "Function 'io/file-set-readable' does not allow %s as f"); + + final boolean on = Coerce.toVncBoolean(args.second()).getValue(); + final boolean ownerOnly = Coerce.toVncBoolean(args.third()).getValue(); + + return VncBoolean.of(f.setWritable(on, ownerOnly)); + } + + private static final long serialVersionUID = -1848883965231344442L; + }; + + + public static VncFunction io_file_set_executable = + new VncFunction( + "io/file-set-executable", + VncFunction + .meta() + .arglists("(io/file-set-executable f executable owner-only)") + .doc( + "Set the owner’s execute permission to the file or directory f. " + + "f must be a file or a string (file path)." + + "\n\n" + + "Returns true if and only if the operation succeeded. The " + + "operation will fail if the user does not have permission to " + + "change the access permissions of this abstract pathname. If " + + "'readable' is false and the underlying file system does not " + + "implement a read permission, then the operation will fail." + + "\n\n" + + "If 'executable' is true sets the access permission to allow execute " + + "operations; if false to disallow execute operations. " + + "\n\n" + + "If 'owner-only' is true the execute permission applies only to the " + + "owner's execute permission; otherwise, it applies to everybody. If " + + "the underlying file system can not distinguish the owner's execute " + + "permission from that of others, then the permission will apply to " + + "everybody, regardless of this value.") + .examples( + "(io/file-set-executable \"/tmp/test.txt\" true true)") + .seeAlso( + "io/file-can-execute?", + "io/file-set-readable", + "io/file-set-writable") + .build() + ) { + @Override + public VncVal apply(final VncList args) { + ArityExceptions.assertArity(this, args, 3); + + final File f = convertToFile( + args.first(), + "Function 'io/file-set-executable' does not allow %s as f"); + + final boolean on = Coerce.toVncBoolean(args.second()).getValue(); + final boolean ownerOnly = Coerce.toVncBoolean(args.third()).getValue(); + + return VncBoolean.of(f.setExecutable(on, ownerOnly)); + } + + private static final long serialVersionUID = -1848883965231344442L; + }; + public static VncFunction io_file_hidden_Q = new VncFunction( "io/file-hidden?", @@ -676,7 +836,10 @@ public VncVal apply(final VncList args) { .examples( "(io/file-hidden? \"/tmp/test.txt\")") .seeAlso( - "io/file-can-read?", "io/file-can-write?", "io/file-can-execute?", "io/file-symbolic-link?") + "io/file-can-read?", + "io/file-can-write?", + "io/file-can-execute?", + "io/file-symbolic-link?") .build() ) { @Override @@ -705,7 +868,10 @@ public VncVal apply(final VncList args) { .examples( "(io/file-symbolic-link? \"/tmp/test.txt\")") .seeAlso( - "io/file-hidden?", "io/file-can-read?", "io/file-can-write?", "io/file-can-execute?") + "io/file-hidden?", + "io/file-can-read?", + "io/file-can-write?", + "io/file-can-execute?") .build() ) { @Override @@ -714,7 +880,7 @@ public VncVal apply(final VncList args) { final File f = convertToFile( args.first(), - "Function 'io/symbolic-link?' does not allow %s as f"); + "Function 'io/file-symbolic-link?' does not allow %s as f"); final Path p = f.toPath(); @@ -724,6 +890,92 @@ public VncVal apply(final VncList args) { private static final long serialVersionUID = -1848883965231344442L; }; + public static VncFunction io_file_create_symbolic_link = + new VncFunction( + "io/file-create-symbolic-link", + VncFunction + .meta() + .arglists("(io/file-create-symbolic-link link target)") + .doc( + "Creates a symbolic link to a target. \n" + + "link and target must be a file or a string (file path).") + .examples( + "(io/file-create-symbolic-link \"/tmp/sym-link\" \"/tmp/test.txt\")") + .seeAlso( + "io/file-create-hard-link", + "io/file-symbolic-link?") + .build() + ) { + @Override + public VncVal apply(final VncList args) { + ArityExceptions.assertArity(this, args, 2); + + final File link = convertToFile( + args.first(), + "Function 'io/file-create-symbolic-link' does not allow %s as link"); + + final File target = convertToFile( + args.second(), + "Function 'io/file-create-symbolic-link' does not allow %s as target"); + + try { + Files.createSymbolicLink(link.toPath(), target.toPath()); + return Nil; + } + catch(Exception ex) { + throw new VncException( + String.format("Failed to create symbolic link %s -> %s", link, target), + ex); + + } + } + + private static final long serialVersionUID = -1848883965231344442L; + }; + + public static VncFunction io_file_create_hard_link = + new VncFunction( + "io/file-create-hard-link", + VncFunction + .meta() + .arglists("(io/file-create-hard-link link target)") + .doc( + "Creates a hard link to a target. \n" + + "link and target must be a file or a string (file path).") + .examples( + "(io/file-create-hard-link \"/tmp/hard-link\" \"/tmp/test.txt\")") + .seeAlso( + "io/file-create-symbolic-link", + "io/file-symbolic-link?") + .build() + ) { + @Override + public VncVal apply(final VncList args) { + ArityExceptions.assertArity(this, args, 2); + + final File link = convertToFile( + args.first(), + "Function 'io/file-create-hard-link' does not allow %s as link"); + + final File target = convertToFile( + args.second(), + "Function 'io/file-create-hard-link' does not allow %s as target"); + + try { + Files.createLink(link.toPath(), target.toPath()); + return Nil; + } + catch(Exception ex) { + throw new VncException( + String.format("Failed to create hard link %s -> %s", link, target), + ex); + + } + } + + private static final long serialVersionUID = -1848883965231344442L; + }; + public static VncFunction io_file_last_modified = new VncFunction( "io/file-last-modified", @@ -736,7 +988,9 @@ public VncVal apply(final VncList args) { .examples( "(io/file-last-modified \"/tmp/test.txt\")") .seeAlso( - "io/file-can-read?", "io/file-can-write?", "io/file-can-execute?") + "io/file-can-read?", + "io/file-can-write?", + "io/file-can-execute?") .build() ) { @Override @@ -1951,9 +2205,9 @@ public VncVal apply(final VncList args) { try (DirectoryStream dirStream = Files.newDirectoryStream(srcdir.toPath(), glob)) { dirStream.forEach(path -> { try { - final Path d = dstdir.toPath().resolve(srcdir.toPath().relativize(path)); + final Path d = dstdir.toPath().resolve(srcdir.toPath().relativize(path)); - Files.copy( + Files.copy( path, d, copyOptions.toArray(new CopyOption[0])); @@ -2205,7 +2459,7 @@ public VncVal apply(final VncList args) { validateReadableDirectory(srcdir); validateWritableDirectory(dstdir); - final List moveOptions = new ArrayList<>(); + final List moveOptions = new ArrayList<>(); if (VncBoolean.isTrue(replaceOpt)) { moveOptions.add(StandardCopyOption.REPLACE_EXISTING); } @@ -2216,7 +2470,7 @@ public VncVal apply(final VncList args) { try (DirectoryStream dirStream = Files.newDirectoryStream(srcdir.toPath(), glob)) { dirStream.forEach(path -> { try { - final Path d = dstdir.toPath().resolve(srcdir.toPath().relativize(path)); + final Path d = dstdir.toPath().resolve(srcdir.toPath().relativize(path)); Files.move( path, @@ -3172,7 +3426,12 @@ private static final String globPatternHelp() { .add(io_file_can_read_Q) .add(io_file_can_write_Q) .add(io_file_can_execute_Q) + .add(io_file_set_readable) + .add(io_file_set_writable) + .add(io_file_set_executable) .add(io_file_hidden_Q) + .add(io_file_create_symbolic_link) + .add(io_file_create_hard_link) .add(io_file_symbolicl_link_Q) .add(io_file_absolute_Q) .add(io_glob_path_matcher)