Files

File tree

A folder's files, recursively, with sizes and dates: listings with limits, a folder's total size, and its largest and newest files.

Version 1.0.0 · 4 functions · 1 crate

#files#folders#admin

Functions

What use file_tree.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use file_tree, file_tree_size from file_tree (Quillo 0.6).

file_tree(dir: str, max_depth: int, hidden: bool) → listor an error

Everything under the folder dir, folders first-to-last in name order: { path, name, kind, size, modified, depth }. path is relative to dir; kind is file, dir or link (links are not followed); size in bytes; modified in seconds since 1970. max_depth 1 lists only dir itself, 0 goes all the way down; hidden false leaves out names starting with a dot. At most 100,000 entries.

file_tree_size(dir: str) → anyor an error

The folder's totals, hidden files included: { files, dirs, bytes }.

file_tree_largest(dir: str, count: int) → listor an error

The count largest files under the folder (up to 1,000), largest first: to find what fills a disk.

file_tree_newest(dir: str, count: int) → listor an error

The count files changed last under the folder (up to 1,000), newest first: for "recent uploads" or a sync.

Get it

$ quillo install file_tree               # file_tree.qwer into this folder$ quillo install file_tree --example     # and its example, file_tree.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/file_tree.qwer$ curl -O https://quillolang.org/plugins/files/file_tree.qwe

Then, in your program, all of it or only what you call:

main.qwe
use file_tree.qweruse file_tree, file_tree_size from file_tree      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
walkdir = "2"

Example

file_tree.qwe
// A file manager's page for an uploads folder: the tree, its size, and the biggest files.////   quillo run file_tree.qwe      then open http://localhost:8080/filesuse "file_tree.qwer"fn seed() {    write_file("data/library/reports/2026-q3.csv", "region,total\nNorth,18250\nSouth,16410\n")    write_file("data/library/reports/2026-q2.csv", "region,total\nNorth,17000\n")    write_file("data/library/photos/shop.txt", repeat("a picture would be here ", 400))    write_file("data/library/.cache/x", "hidden")    write_file("data/library/readme.md", "# Library\n")}route GET /files {    seed()    {        tree: file_tree("data/library", 0, false)? | map((e) => { path: e.path, kind: e.kind, size: e.size }),        size: file_tree_size("data/library")?,        largest: file_tree_largest("data/library", 1)? | map((e) => e.path),    }}route GET /top = file_tree("data/library", 1, false)? | map((e) => e.name)route GET / {    seed()    <html><body><ul>        {for e in file_tree("data/library", 0, false)? => <li style={`margin-left: ${e.depth}em`}>{e.name} {e.kind == "file" ? `(${e.size} bytes)` : ""}</li>}    </ul></body></html>}
$ quillo run file_tree.qwe$ curl 'localhost:8080/files'$ curl 'localhost:8080/top'

Source

file_tree.qwer
//! A folder's files, recursively, with their sizes and dates: listings with limits, a folder's total size, and its largest and newest files.//! version: 1.0.0//!//! [dependencies]//! walkdir = "2"use serde_json::{json, Value};use std::time::UNIX_EPOCH;const MAX_ENTRIES: usize = 100_000;fn hidden(e: &walkdir::DirEntry) -> bool {    e.depth() > 0 && e.file_name().to_str().map_or(false, |s| s.starts_with('.'))}// The folder's entries below it (not following links), hidden ones left out unless asked, at most 100,000.fn entries(dir: &str, max_depth: i64, show_hidden: bool) -> Result<Vec<walkdir::DirEntry>, String> {    if !std::path::Path::new(dir).is_dir() {        return Err(format!("{}: not a folder", dir));    }    let depth = if max_depth <= 0 { usize::MAX } else { max_depth as usize };    let mut out = vec![];    for e in walkdir::WalkDir::new(dir).min_depth(1).max_depth(depth).follow_links(false).sort_by_file_name().into_iter().filter_entry(|e| show_hidden || !hidden(e)) {        let Ok(e) = e else { continue };        out.push(e);        if out.len() >= MAX_ENTRIES {            break;        }    }    Ok(out)}fn record(dir: &str, e: &walkdir::DirEntry) -> Value {    let meta = e.metadata().ok();    let kind = if e.file_type().is_dir() { "dir" } else if e.file_type().is_symlink() { "link" } else { "file" };    let rel = e.path().strip_prefix(dir).unwrap_or(e.path()).to_string_lossy().replace('\\', "/");    json!({        "path": rel,        "name": e.file_name().to_string_lossy(),        "kind": kind,        "size": if kind == "file" { meta.as_ref().map_or(0, |m| m.len()) } else { 0 },        "modified": meta.and_then(|m| m.modified().ok()).and_then(|t| t.duration_since(UNIX_EPOCH).ok()).map_or(0, |d| d.as_secs()),        "depth": e.depth(),    })}/// Everything under the folder `dir`, folders first-to-last in name order: `{ path, name, kind, size, modified, depth }`./// `path` is relative to `dir`; `kind` is file, dir or link (links are not followed); `size` in bytes; `modified` in seconds since 1970./// `max_depth` 1 lists only `dir` itself, 0 goes all the way down; `hidden` false leaves out names starting with a dot. At most 100,000 entries.pub fn file_tree(dir: &str, max_depth: i64, hidden: bool) -> Result<Vec<Value>, String> {    Ok(entries(dir, max_depth, hidden)?.iter().map(|e| record(dir, e)).collect())}/// The folder's totals, hidden files included: `{ files, dirs, bytes }`.pub fn file_tree_size(dir: &str) -> Result<Value, String> {    let (mut files, mut dirs, mut bytes) = (0u64, 0u64, 0u64);    for e in entries(dir, 0, true)? {        if e.file_type().is_dir() {            dirs += 1;        } else if e.file_type().is_file() {            files += 1;            bytes += e.metadata().map_or(0, |m| m.len());        }    }    Ok(json!({ "files": files, "dirs": dirs, "bytes": bytes }))}/// The `count` largest files under the folder (up to 1,000), largest first: to find what fills a disk.pub fn file_tree_largest(dir: &str, count: i64) -> Result<Vec<Value>, String> {    let mut files: Vec<Value> = entries(dir, 0, true)?.iter().filter(|e| e.file_type().is_file()).map(|e| record(dir, e)).collect();    files.sort_by(|a, b| b["size"].as_u64().cmp(&a["size"].as_u64()));    files.truncate(count.clamp(1, 1000) as usize);    Ok(files)}/// The `count` files changed last under the folder (up to 1,000), newest first: for "recent uploads" or a sync.pub fn file_tree_newest(dir: &str, count: i64) -> Result<Vec<Value>, String> {    let mut files: Vec<Value> = entries(dir, 0, false)?.iter().filter(|e| e.file_type().is_file()).map(|e| record(dir, e)).collect();    files.sort_by(|a, b| b["modified"].as_u64().cmp(&a["modified"].as_u64()).then(a["path"].as_str().cmp(&b["path"].as_str())));    files.truncate(count.clamp(1, 1000) as usize);    Ok(files)}
Thread
96Useful? Like it, or add to its thread below.

Thread 0 posts

On File tree: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.