From 396cde5f0a5c2a013e5928c187738491e48a8ba9 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 5 Feb 2025 23:38:20 +0100 Subject: [PATCH 1/7] example: simplify transfer example --- examples/transfer.rs | 136 +++++++++++++++++++++++++++---------------- src/hash.rs | 11 ++++ 2 files changed, 96 insertions(+), 51 deletions(-) diff --git a/examples/transfer.rs b/examples/transfer.rs index 63e156579..fd24c955c 100644 --- a/examples/transfer.rs +++ b/examples/transfer.rs @@ -1,10 +1,11 @@ -use std::{path::PathBuf, str::FromStr}; +use std::path::PathBuf; -use anyhow::Result; +use anyhow::{bail, Result}; use iroh::{protocol::Router, Endpoint}; use iroh_blobs::{ net_protocol::Blobs, - rpc::client::blobs::{ReadAtLen, WrapOption}, + rpc::client::blobs::{self, WrapOption}, + store::ExportMode, ticket::BlobTicket, util::SetTagOption, }; @@ -19,70 +20,103 @@ async fn main() -> Result<()> { // Now we build a router that accepts blobs connections & routes them // to the blobs protocol. - let node = Router::builder(endpoint) + let router = Router::builder(endpoint) .accept(iroh_blobs::ALPN, blobs.clone()) .spawn() .await?; - let blobs = blobs.client(); - - let args = std::env::args().collect::>(); - match &args.iter().map(String::as_str).collect::>()[..] { - [_cmd, "send", path] => { - let abs_path = PathBuf::from_str(path)?.canonicalize()?; + // Grab all passed in arguments, the first one is the binary itself, so we skip it. + let args: Vec<_> = std::env::args().skip(1).collect(); + if args.len() < 2 { + print_usage(); + bail!("too few arguments"); + } - println!("Analyzing file."); + match &*args[0] { + "send" => { + send(&router, blobs.client(), &args).await?; - let blob = blobs - .add_from_path(abs_path, true, SetTagOption::Auto, WrapOption::NoWrap) - .await? - .finish() - .await?; + tokio::signal::ctrl_c().await?; + } + "receive" => { + receive(blobs.client(), &args).await?; + } + cmd => { + print_usage(); + bail!("unkown command {}", cmd); + } + } - let node_id = node.endpoint().node_id(); - let ticket = BlobTicket::new(node_id.into(), blob.hash, blob.format)?; + // Gracefully shut down the node + println!("Shutting down."); + router.shutdown().await?; - println!("File analyzed. Fetch this file by running:"); - println!("cargo run --example transfer -- receive {ticket} {path}"); + Ok(()) +} - tokio::signal::ctrl_c().await?; - } - [_cmd, "receive", ticket, path] => { - let path_buf = PathBuf::from_str(path)?; - let ticket = BlobTicket::from_str(ticket)?; +async fn send(router: &Router, blobs: &blobs::MemClient, args: &[String]) -> Result<()> { + let path: PathBuf = args[1].parse()?; + let abs_path = path.canonicalize()?; - println!("Starting download."); + println!("Analyzing file."); - blobs - .download(ticket.hash(), ticket.node_addr().clone()) - .await? - .finish() - .await?; + // keep the file in place, and link it + let in_place = true; + let blob = blobs + .add_from_path(abs_path, in_place, SetTagOption::Auto, WrapOption::NoWrap) + .await? + .await?; - println!("Finished download."); - println!("Copying to destination."); + let node_id = router.endpoint().node_id(); + let ticket = BlobTicket::new(node_id.into(), blob.hash, blob.format)?; - let mut file = tokio::fs::File::create(path_buf).await?; - let mut reader = blobs.read_at(ticket.hash(), 0, ReadAtLen::All).await?; - tokio::io::copy(&mut reader, &mut file).await?; + println!("File analyzed. Fetch this file by running:"); + println!( + "cargo run --example transfer -- receive {ticket} {}", + path.display() + ); + Ok(()) +} - println!("Finished copying."); - } - _ => { - println!("Couldn't parse command line arguments."); - println!("Usage:"); - println!(" # to send:"); - println!(" cargo run --example transfer -- send [FILE]"); - println!(" # this will print a ticket."); - println!(); - println!(" # to receive:"); - println!(" cargo run --example transfer -- receive [TICKET] [FILE]"); - } +async fn receive(blobs: &blobs::MemClient, args: &[String]) -> Result<()> { + if args.len() < 3 { + print_usage(); + bail!("too few arguments"); } + let path_buf: PathBuf = args[1].parse()?; + let ticket: BlobTicket = args[2].parse()?; - // Gracefully shut down the node - println!("Shutting down."); - node.shutdown().await?; + println!("Starting download."); + + blobs + .download(ticket.hash(), ticket.node_addr().clone()) + .await? + .await?; + + println!("Finished download."); + println!("Copying to destination."); + + blobs + .export( + ticket.hash(), + path_buf, + ticket.format().into(), + ExportMode::Copy, + ) + .await?; + + println!("Finished copying."); Ok(()) } + +fn print_usage() { + println!("Couldn't parse command line arguments."); + println!("Usage:"); + println!(" # to send:"); + println!(" cargo run --example transfer -- send [FILE]"); + println!(" # this will print a ticket."); + println!(); + println!(" # to receive:"); + println!(" cargo run --example transfer -- receive [TICKET] [FILE]"); +} diff --git a/src/hash.rs b/src/hash.rs index 95190793c..53a5f92c3 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -5,6 +5,8 @@ use std::{borrow::Borrow, fmt, str::FromStr}; use postcard::experimental::max_size::MaxSize; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use crate::store::ExportFormat; + /// Hash type used throughout. #[derive(PartialEq, Eq, Copy, Clone, Hash)] pub struct Hash(blake3::Hash); @@ -242,6 +244,15 @@ impl BlobFormat { } } +impl From for ExportFormat { + fn from(value: BlobFormat) -> Self { + match value { + BlobFormat::Raw => ExportFormat::Blob, + BlobFormat::HashSeq => ExportFormat::Collection, + } + } +} + /// A hash and format pair #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, MaxSize, Hash)] pub struct HashAndFormat { From 5a92c083fe813ca12e957f71d515a01252d15c37 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 5 Feb 2025 23:42:37 +0100 Subject: [PATCH 2/7] typing is hard --- examples/transfer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/transfer.rs b/examples/transfer.rs index fd24c955c..46e1aadfd 100644 --- a/examples/transfer.rs +++ b/examples/transfer.rs @@ -43,7 +43,7 @@ async fn main() -> Result<()> { } cmd => { print_usage(); - bail!("unkown command {}", cmd); + bail!("unknown command {}", cmd); } } From b3602a0e97d48f7fe63bf172e089a499a156cdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 14 Feb 2025 12:25:04 +0100 Subject: [PATCH 3/7] Undo extracting into functions --- examples/transfer.rs | 152 +++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 84 deletions(-) diff --git a/examples/transfer.rs b/examples/transfer.rs index 46e1aadfd..51ec05e40 100644 --- a/examples/transfer.rs +++ b/examples/transfer.rs @@ -1,11 +1,11 @@ use std::path::PathBuf; -use anyhow::{bail, Result}; +use anyhow::Result; use iroh::{protocol::Router, Endpoint}; use iroh_blobs::{ net_protocol::Blobs, - rpc::client::blobs::{self, WrapOption}, - store::ExportMode, + rpc::client::blobs::WrapOption, + store::{ExportFormat, ExportMode}, ticket::BlobTicket, util::SetTagOption, }; @@ -26,24 +26,75 @@ async fn main() -> Result<()> { .await?; // Grab all passed in arguments, the first one is the binary itself, so we skip it. - let args: Vec<_> = std::env::args().skip(1).collect(); - if args.len() < 2 { - print_usage(); - bail!("too few arguments"); - } - - match &*args[0] { - "send" => { - send(&router, blobs.client(), &args).await?; + let args: Vec = std::env::args().skip(1).collect(); + // Convert to &str, so we can pattern-match easily: + let arg_refs: Vec<&str> = args.iter().map(String::as_str).collect(); + + match arg_refs.as_slice() { + ["send", filename] => { + let filename: PathBuf = filename.parse()?; + let abs_path = std::path::absolute(&filename)?; + + println!("Analyzing file."); + + // keep the file in place and link it, instead of copying it into the in-memory blobs database + let in_place = true; + let blob = blobs + .client() + .add_from_path(abs_path, in_place, SetTagOption::Auto, WrapOption::NoWrap) + .await? + .await?; + + let node_id = router.endpoint().node_id(); + let ticket = BlobTicket::new(node_id.into(), blob.hash, blob.format)?; + + println!("File analyzed. Fetch this file by running:"); + println!( + "cargo run --example transfer -- receive {ticket} {}", + filename.display() + ); tokio::signal::ctrl_c().await?; } - "receive" => { - receive(blobs.client(), &args).await?; + ["receive", ticket, filename] => { + let filename: PathBuf = filename.parse()?; + let abs_path = std::path::absolute(filename)?; + let ticket: BlobTicket = ticket.parse()?; + + println!("Starting download."); + + blobs + .client() + .download(ticket.hash(), ticket.node_addr().clone()) + .await? + .await?; + + println!("Finished download."); + println!("Copying to destination."); + + blobs + .client() + .export( + ticket.hash(), + abs_path, + ExportFormat::Blob, + ExportMode::Copy, + ) + .await? + .finish() + .await?; + + println!("Finished copying."); } - cmd => { - print_usage(); - bail!("unknown command {}", cmd); + _ => { + println!("Couldn't parse command line arguments: {args:?}"); + println!("Usage:"); + println!(" # to send:"); + println!(" cargo run --example transfer -- send [FILE]"); + println!(" # this will print a ticket."); + println!(); + println!(" # to receive:"); + println!(" cargo run --example transfer -- receive [TICKET] [FILE]"); } } @@ -53,70 +104,3 @@ async fn main() -> Result<()> { Ok(()) } - -async fn send(router: &Router, blobs: &blobs::MemClient, args: &[String]) -> Result<()> { - let path: PathBuf = args[1].parse()?; - let abs_path = path.canonicalize()?; - - println!("Analyzing file."); - - // keep the file in place, and link it - let in_place = true; - let blob = blobs - .add_from_path(abs_path, in_place, SetTagOption::Auto, WrapOption::NoWrap) - .await? - .await?; - - let node_id = router.endpoint().node_id(); - let ticket = BlobTicket::new(node_id.into(), blob.hash, blob.format)?; - - println!("File analyzed. Fetch this file by running:"); - println!( - "cargo run --example transfer -- receive {ticket} {}", - path.display() - ); - Ok(()) -} - -async fn receive(blobs: &blobs::MemClient, args: &[String]) -> Result<()> { - if args.len() < 3 { - print_usage(); - bail!("too few arguments"); - } - let path_buf: PathBuf = args[1].parse()?; - let ticket: BlobTicket = args[2].parse()?; - - println!("Starting download."); - - blobs - .download(ticket.hash(), ticket.node_addr().clone()) - .await? - .await?; - - println!("Finished download."); - println!("Copying to destination."); - - blobs - .export( - ticket.hash(), - path_buf, - ticket.format().into(), - ExportMode::Copy, - ) - .await?; - - println!("Finished copying."); - - Ok(()) -} - -fn print_usage() { - println!("Couldn't parse command line arguments."); - println!("Usage:"); - println!(" # to send:"); - println!(" cargo run --example transfer -- send [FILE]"); - println!(" # this will print a ticket."); - println!(); - println!(" # to receive:"); - println!(" cargo run --example transfer -- receive [TICKET] [FILE]"); -} From 5601286ab06f27ef8df89d9d714b35d51d18b3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 14 Feb 2025 12:29:30 +0100 Subject: [PATCH 4/7] Add `.finish()` call for clarity --- examples/transfer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/transfer.rs b/examples/transfer.rs index 51ec05e40..c2f31927d 100644 --- a/examples/transfer.rs +++ b/examples/transfer.rs @@ -43,6 +43,7 @@ async fn main() -> Result<()> { .client() .add_from_path(abs_path, in_place, SetTagOption::Auto, WrapOption::NoWrap) .await? + .finish() .await?; let node_id = router.endpoint().node_id(); From 50c699c629502e0309139bd8bb0d830e82c140d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 14 Feb 2025 12:30:39 +0100 Subject: [PATCH 5/7] Use `.finish()` for `*Progress` structs --- examples/transfer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/transfer.rs b/examples/transfer.rs index c2f31927d..3b1af25fc 100644 --- a/examples/transfer.rs +++ b/examples/transfer.rs @@ -68,6 +68,7 @@ async fn main() -> Result<()> { .client() .download(ticket.hash(), ticket.node_addr().clone()) .await? + .finish() .await?; println!("Finished download."); From d2bb3adf476a118f22f4a0cb8b69ec6d59111ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 14 Feb 2025 16:02:14 +0100 Subject: [PATCH 6/7] More small adjustments --- examples/transfer.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/transfer.rs b/examples/transfer.rs index 3b1af25fc..960274db7 100644 --- a/examples/transfer.rs +++ b/examples/transfer.rs @@ -25,6 +25,9 @@ async fn main() -> Result<()> { .spawn() .await?; + // We use a blobs client to interact with the blobs protocol we're running locally: + let blobs_client = blobs.client(); + // Grab all passed in arguments, the first one is the binary itself, so we skip it. let args: Vec = std::env::args().skip(1).collect(); // Convert to &str, so we can pattern-match easily: @@ -35,12 +38,11 @@ async fn main() -> Result<()> { let filename: PathBuf = filename.parse()?; let abs_path = std::path::absolute(&filename)?; - println!("Analyzing file."); + println!("Hashing file."); // keep the file in place and link it, instead of copying it into the in-memory blobs database let in_place = true; - let blob = blobs - .client() + let blob = blobs_client .add_from_path(abs_path, in_place, SetTagOption::Auto, WrapOption::NoWrap) .await? .finish() @@ -49,7 +51,7 @@ async fn main() -> Result<()> { let node_id = router.endpoint().node_id(); let ticket = BlobTicket::new(node_id.into(), blob.hash, blob.format)?; - println!("File analyzed. Fetch this file by running:"); + println!("File hashed. Fetch this file by running:"); println!( "cargo run --example transfer -- receive {ticket} {}", filename.display() @@ -64,8 +66,7 @@ async fn main() -> Result<()> { println!("Starting download."); - blobs - .client() + blobs_client .download(ticket.hash(), ticket.node_addr().clone()) .await? .finish() @@ -74,8 +75,7 @@ async fn main() -> Result<()> { println!("Finished download."); println!("Copying to destination."); - blobs - .client() + blobs_client .export( ticket.hash(), abs_path, From 207488e5ac364ff71dd9f09bdff4fd8f0fa842dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 21 Feb 2025 10:46:55 +0100 Subject: [PATCH 7/7] Remove `impl From for ExportFormat` --- src/hash.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/hash.rs b/src/hash.rs index 53a5f92c3..95190793c 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -5,8 +5,6 @@ use std::{borrow::Borrow, fmt, str::FromStr}; use postcard::experimental::max_size::MaxSize; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use crate::store::ExportFormat; - /// Hash type used throughout. #[derive(PartialEq, Eq, Copy, Clone, Hash)] pub struct Hash(blake3::Hash); @@ -244,15 +242,6 @@ impl BlobFormat { } } -impl From for ExportFormat { - fn from(value: BlobFormat) -> Self { - match value { - BlobFormat::Raw => ExportFormat::Blob, - BlobFormat::HashSeq => ExportFormat::Collection, - } - } -} - /// A hash and format pair #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, MaxSize, Hash)] pub struct HashAndFormat {