use async_recursion::async_recursion;
use color_eyre::eyre::{eyre, Result};
use color_eyre::owo_colors::OwoColorize;
use futures::{pin_mut, StreamExt};
use crate::files::CoderChannel;
use chris::types::{FileBrowserPath, FileResourceFname};
use chris::{FileBrowser, RoClient};
use crate::ls::options::WhatToPrint;
pub async fn ls_plain(
client: &RoClient,
path: &str,
level: u16,
full: bool,
mut coder: CoderChannel,
what_to_print: WhatToPrint,
) -> Result<()> {
let relative_parent = if full {
None
} else {
Some(coder.decode(path.to_string()).await)
};
let was = ls_recursive(
client.filebrowser(),
path.into(),
level,
&relative_parent,
&mut coder,
what_to_print,
Default::default(),
)
.await?;
if !was.printed && was.had_subdirs {
let mut cmd: Vec<String> = std::env::args().collect();
cmd.insert(cmd.len() - 1, "--show=folders".to_string());
eprintln!(
"Path contains subfolders but no files. To show directories, run `{}`",
cmd.join(" ").bold()
)
}
Ok(())
}
#[async_recursion]
async fn ls_recursive(
fb: FileBrowser,
path: FileBrowserPath,
level: u16,
relative_parent: &Option<String>,
coder: &mut CoderChannel,
what_to_print: WhatToPrint,
mut was: WasPrinted,
) -> Result<WasPrinted> {
if level == 0 {
return Ok(was);
}
let entry = fb
.readdir(&path)
.await?
.ok_or_else(|| eyre!("Path not found: {}", path))?;
was.had_subdirs = was.had_subdirs || !entry.subfolders().is_empty();
if what_to_print.should_print_folders() {
for subfolder in entry.absolute_subfolders() {
print_path(coder, subfolder.take(), relative_parent, true).await?;
was.printed = true;
}
}
if what_to_print.should_print_files() {
let iter_files = entry.iter_files();
let files_stream = iter_files.stream();
pin_mut!(files_stream);
while let Some(file_result) = files_stream.next().await {
let file_path: FileResourceFname = file_result?.into();
print_path(coder, file_path.take(), relative_parent, false).await?;
was.printed = true;
}
}
for subfolder in entry.absolute_subfolders() {
let sub_was = ls_recursive(
fb.clone(),
subfolder,
level - 1,
relative_parent,
coder,
what_to_print,
was,
)
.await?;
was = was.reduce(sub_was);
}
Ok(was)
}
async fn print_path(
coder: &mut CoderChannel,
fnamelike: String,
relative_parent: &Option<String>,
is_dir: bool,
) -> Result<()> {
let relative_parent_len = relative_parent.as_ref().map(|s| s.len() + 1).unwrap_or(0);
let ez_path = coder.decode(fnamelike).await;
let rel_path = ez_path.get(relative_parent_len..).ok_or_else(|| {
eyre!(
"CUBE returned a file path \"{}\" which is not a subpath of parent {:?}",
&ez_path,
&relative_parent.as_slice()
)
})?;
if is_dir {
print_dir(rel_path)
} else {
print_file(rel_path)
}
Ok(())
}
fn print_dir(path: &str) {
println!("{}/", path.blue())
}
fn print_file(path: &str) {
let colored = path
.rsplit_once('/')
.map(|(dir, file)| format!("{}/{}", dir.blue(), file))
.unwrap_or_else(|| path.to_string());
println!("{}", colored)
}
#[derive(Default, Clone, Copy)]
struct WasPrinted {
printed: bool,
had_subdirs: bool,
}
impl WasPrinted {
fn reduce(self, other: Self) -> Self {
Self {
printed: self.printed || other.printed,
had_subdirs: self.had_subdirs || other.had_subdirs,
}
}
}