claps 0.2.0

CLIs written in Rust
Documentation
use std::fmt::Display;
use std::path::{Path, PathBuf};

use anyhow::Result;
use clap::Args;

use claps::api::alist::Client;

#[derive(Debug, Args)]
pub(super) struct Cmd {
    #[arg(default_value = "/")]
    path: PathBuf,
    #[arg(short, long, default_value = "1")]
    depth: i64,
    #[arg(short, long)]
    refresh: bool,
}

impl Cmd {
    pub async fn run(self, args: crate::cmd::CommonArgs) -> Result<()> {
        let mut client = Client::new(args.url.as_str());
        client
            .auth_login(args.username()?.as_str(), args.password()?.as_str())
            .await?;
        println!(
            "{}",
            tree(&client, self.path.as_path(), self.depth, self.refresh).await?
        );
        Ok(())
    }
}

#[async_recursion::async_recursion]
async fn tree(
    client: &Client,
    path: &'async_recursion Path,
    depth: i64,
    refresh: bool,
) -> Result<Tree> {
    let data = client
        .fs_list(Some(path.to_str().unwrap()), None, Some(refresh))
        .await
        .unwrap();
    let children = futures::future::join_all(
        data.content
            .unwrap_or_default()
            .iter()
            .map(|c| async {
                let path = path.join(c.name.as_str());
                if depth > 1 && c.is_dir {
                    return tree(client, path.as_path(), depth - 1, refresh).await.ok();
                }
                Some(Tree::new(path.as_path(), c.is_dir))
            })
            .collect::<Vec<_>>(),
    )
    .await
    .into_iter()
    .flatten()
    .collect::<Vec<_>>();
    Ok(Tree::new(path, true).with_children(children))
}

struct Tree {
    path: PathBuf,
    is_dir: bool,
    children: Vec<Tree>,
}

impl Display for Tree {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.path.display())?;
        if self.is_dir {
            write!(f, "/")?;
        }
        writeln!(f)?;
        for child in self.children.iter() {
            child.fmt(f)?;
        }
        Ok(())
    }
}

impl Tree {
    fn new(path: &Path, is_dir: bool) -> Self {
        Self {
            path: path.to_path_buf(),
            is_dir,
            children: Vec::new(),
        }
    }

    fn with_children<I>(mut self, children: I) -> Self
    where
        I: IntoIterator<Item = Self>,
    {
        self.children = children.into_iter().collect();
        self
    }
}