use clap::Parser;
use indicatif::{ProgressBar, ProgressStyle};
use megalib::SessionHandle;
use megalib::api::ApiErrorCode;
use megalib::error::{MegaError, Result};
use megalib::progress::TransferProgress;
use std::io::{self, Write};
use tracing_subscriber::{EnvFilter, fmt};
#[derive(Debug, Parser)]
#[command(name = "sequence")]
struct Args {
#[arg(short = 'e', long)]
email: String,
#[arg(short = 'p', long)]
password: String,
#[arg(long)]
proxy: Option<String>,
folder1: Option<String>,
folder2: Option<String>,
local1: Option<String>,
local2: Option<String>,
}
fn init_tracing() {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("off"));
fmt().with_env_filter(filter).with_target(false).init();
}
#[tokio::main]
async fn main() -> Result<()> {
init_tracing();
let args = Args::parse();
let folder1 = args.folder1.unwrap_or_else(|| "/Root/lol1".to_string());
let folder2 = args.folder2.unwrap_or_else(|| "/Root/lol2".to_string());
let local1 = args.local1.unwrap_or_else(|| "./Cargo.toml".to_string());
let local2 = args.local2.unwrap_or_else(|| "./Cargo.lock".to_string());
println!("Logging in...");
let session = if let Some(proxy) = &args.proxy {
SessionHandle::login_with_proxy(&args.email, &args.password, proxy).await?
} else {
SessionHandle::login(&args.email, &args.password).await?
};
let info = session.account_info().await?;
println!("Logged in as: {}", info.email);
let style = ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta}) {msg}",
)
.unwrap_or_else(|_| ProgressStyle::default_bar())
.progress_chars("=>-");
let mut current_file = String::new();
let mut current_bar: Option<ProgressBar> = None;
session
.watch_status(Box::new(move |progress: &TransferProgress| {
if current_bar.is_none() || progress.filename != current_file {
if let Some(previous_bar) = current_bar.take() {
previous_bar.finish_and_clear();
}
current_file = progress.filename.clone();
let bar_len = progress.total.max(1);
let bar = ProgressBar::new(bar_len);
bar.set_style(style.clone());
bar.set_message(current_file.clone());
current_bar = Some(bar);
}
if let Some(bar) = current_bar.as_ref().cloned() {
if progress.total > 0 {
bar.set_length(progress.total);
bar.set_position(progress.done.min(progress.total));
} else {
bar.set_length(progress.done.max(1));
bar.set_position(progress.done);
}
if progress.is_complete() {
bar.finish_with_message(format!("{} complete", progress.filename));
current_bar = None;
current_file.clear();
}
}
true
}))
.await?;
println!("Refreshing filesystem...");
session.refresh().await?;
ensure_folder(&session, &folder1).await?;
upload_and_export(&session, &local1, &folder1).await?;
wait_for_enter("Verify exported link, then press Enter to upload Cargo.lock")?;
upload_and_export(&session, &local2, &folder1).await?;
wait_for_enter("Check both files in folder. Press Enter to continue")?;
ensure_folder(&session, &folder2).await?;
upload_and_export(&session, &local2, &folder2).await?;
wait_for_enter("Verify exported link, then press Enter to upload Cargo.toml")?;
upload_and_export(&session, &local1, &folder2).await?;
println!("Please verify both files are now in the folder.");
println!("Sequence complete.");
Ok(())
}
async fn ensure_folder(session: &SessionHandle, path: &str) -> Result<()> {
match session.mkdir_by_path(path).await {
Ok(_) => {
println!("Created folder: {}", path);
}
Err(MegaError::ApiError { code, .. }) if code == ApiErrorCode::Exist as i32 => {
println!("Folder already exists: {}", path);
}
Err(e) => return Err(e),
}
Ok(())
}
async fn upload_and_export(
session: &SessionHandle,
local: &str,
remote_folder: &str,
) -> Result<()> {
println!("Uploading {local} to {remote_folder} ...");
let remote_path = format!("{}/", remote_folder.trim_end_matches('/'));
let node = session.upload_by_path(local, &remote_path).await?;
println!("Uploaded as {} ({} bytes)", node.name, node.size);
println!("Exporting {} ...", remote_folder);
let link = session.export_by_path(remote_folder).await?;
println!("Public link: {link}");
Ok(())
}
fn wait_for_enter(prompt: &str) -> Result<()> {
print!("{prompt}: ");
io::stdout().flush().ok();
let mut line = String::new();
io::stdin()
.read_line(&mut line)
.map_err(|e| MegaError::Custom(format!("stdin error: {e}")))?;
Ok(())
}