profile
viewpoint
Casey Rodarmor casey The Blue Planet https://rodarmor.com 😻A N A R C H O ー ネ コ バ ス 😻

casey/boot 8

boot / ⠛⠕⠞⠞⠁⠀⠃⠕⠕⠞⠀⠮⠀⠍⠑⠞⠁⠍⠁⠡⠔⠑

casey/bak 3

📦 Move files out of the way

casey/blaster 3

✯ Audio reactive visuals engine in C++/OpenGL

casey/brev 3

💀 Do or die: quick and dirty utility functions in rust

casey/bulb 2

Tulip Farm Discord bot

casey/9x 1

Plan 9 for Unix

casey/annotated-torrents 1

Rich, structured content metadata for BitTorrent

casey/askama 1

Type-safe, compiled Jinja-like templates for Rust

casey/bootstrap 1

Invoke `x.py` slightly more conveniently

casey/clap-config 1

Generate a config parser from a clap argument parser

issue commentcasey/just

Create windows installation package

Thanks for creating this issue! I think it's useful to track creating a windows installer. Do you happen to know which type of installer is easiest to create?

Ideally one that can be used on GitHub Actions to create an installer from a text manifest that can be committed to the repo.

vmiheer

comment created time in an hour

startedEmbarkStudios/rust-gpu

started time in a day

pull request commentcasey/intermodal

Add Void Linux package to README

Merged! Thank you very much!

ThatNerdyPikachu

comment created time in 3 days

PR merged casey/intermodal

Add Void Linux package to README

I'll undraft this once https://github.com/void-linux/void-packages/pull/24520 is merged.

+12 -6

3 comments

2 changed files

ThatNerdyPikachu

pr closed time in 3 days

push eventcasey/intermodal

Pika

commit sha 05ba87039b6bd0044a4c6f3e19afc0843022e4a6

Add Void Linux package to README type: documentation

view details

push time in 3 days

push eventThatNerdyPikachu/intermodal

Pika

commit sha 05ba87039b6bd0044a4c6f3e19afc0843022e4a6

Add Void Linux package to README type: documentation

view details

push time in 3 days

Pull request review commentcasey/intermodal

Announce

+use super::*;+use crate::common::*;++pub(crate) struct Client {+  peer_id: [u8; 20],+  sock: UdpSocket,+  state: State,+}++impl<'a> Client {+  const UDP_TRACKER_MAGIC: u64 = 0x0000_0417_2710_1980;++  pub fn new(peer_id: [u8; 20]) -> Result<Self> {+    Ok(Self {+      peer_id,+      sock: UdpSocket::bind("0.0.0.0:0").context(error::Network)?,+      state: State::Disconnected,+    })+  }++  pub fn connect(&mut self, host_port: &HostPort) -> Result<()> {+    let mut rng = rand::thread_rng();++    self.sock.connect(host_port).context(error::Network)?;++    let req = connect::Request {+      protocol_id: Self::UDP_TRACKER_MAGIC,+      action: 0x0000,+      transaction_id: rng.gen(),+    };++    let mut buf = [0u8; mem::size_of::<connect::Response>()];+    let (resp, _) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }++    self.state = State::Connected {+      id: resp.connection_id,+      epoch: Instant::now(),+      socket_addr: self.sock.peer_addr().context(error::Network)?,+      host_port: host_port.clone(),+    };++    Ok(())+  }++  pub fn announce(&self, btinh: Infohash) -> Result<Vec<SocketAddr>> {+    let mut rng = rand::thread_rng();+    let req = announce::Request {+      connection_id: self.state.get_connection_id()?,+      action: 0x0001,+      transaction_id: rng.gen(),+      infohash: btinh.into(),+      peer_id: self.peer_id,+      downloaded: 0x0000,+      left: u64::MAX,+      uploaded: 0x0000,+      event: 0x0000,+      ip_address: 0x0000,+      num_want: u32::MAX,+      port: self.sock.local_addr().context(error::Network)?.port(),+    };+    let mut buf = [0u8; 8192]; // is there a BUFSIZ macro for Rust?+    let (resp, len) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }+    self.parse_compact_peer_list(&buf[mem::size_of::<announce::Response>()..len])+  }++  fn roundtrip<T: Request>(&self, req: &T, rxbuf: &mut [u8]) -> Result<(T::Response, usize)> {

Instead of returning a usize, can send_and_retry_with_backoff and roundtrip return a &[u8], which is the written-to portion of the rxbuf? This would avoid needing to index into the rxbuf correctly.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use crate::common::*;++const INPUT_HELP: &str =+  "Read torrent metainfo from `INPUT`. If `INPUT` is `-`, read metainfo from standard input.";++const INPUT_FLAG: &str = "input-flag";++const INPUT_POSITIONAL: &str = "<INPUT>";++#[derive(StructOpt)]+#[structopt(+  help_message(consts::HELP_MESSAGE),+  version_message(consts::VERSION_MESSAGE),+  about("Announce a .torrent file.")+)]+pub(crate) struct Announce {+  #[structopt(+    name = INPUT_FLAG,+    long = "input",+    short = "i",+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    help = INPUT_HELP,+  )]+  input_flag: Option<InputTarget>,+  #[structopt(+    name = INPUT_POSITIONAL,+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    required_unless = INPUT_FLAG,+    conflicts_with = INPUT_FLAG,+    help = INPUT_HELP,+  )]+  input_positional: Option<InputTarget>,+}++impl Announce {+  pub(crate) fn run(self, env: &mut Env) -> Result<(), Error> {+    let target = xor_args(+      "input_flag",+      &self.input_flag,+      "input_positional",+      &self.input_positional,+    )?;+    let input = env.read(target)?;++    let mut rng = rand::thread_rng();+    let mut peer_list = Vec::new();+    let mut trackers = Vec::new();+    let infohash = Infohash::from_input(&input)?;+    let metainfo = Metainfo::from_input(&input)?;+    let peer_id: [u8; 20] = rng.gen();++    for result in metainfo.trackers() {+      match result {+        Ok(tracker) => trackers.push(tracker),+        Err(err) => errln!(env, "Failed to parse tracker URL: {}", err)?,+      }+    }++    if trackers.is_empty() {+      return Err(Error::MetainfoMissingTrackers);+    }++    let mut client = tracker::Client::new(peer_id)?;+    errln!(env, "[1/2] Announcing {} to trackers.", &infohash)?;++    for tracker in trackers {+      if tracker.scheme() == "udp" {+        let hostport = match HostPort::from_url(&tracker) {+          Some(hostport) => hostport,+          None => continue,+        };++        if let Err(err) = client.connect(&hostport) {+          errln!(env, "[...] Connection to {} failed: {}", hostport, err)?;+        }++        match client.announce(infohash) {+          Ok(subswarm) => {+            peer_list.extend(subswarm);+            break;+          }+          Err(err) => errln!(env, "{:?}", err)?,+        }+      } else {+        errln!(+          env,+          "<info> Only UDP trackers are supported at present; skipping {}.",+          tracker+        )?;+      }+    }++    errln!(env, "[2/2] Done")?;++    for peer in &peer_list {+      outln!(env, "{}", peer)?;+    }++    Ok(())+  }+}++#[cfg(test)]+mod tests {+  use super::*;+

There should be a test that the announce command prints out a peer list.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use super::*;+use crate::common::*;++pub(crate) struct Client {+  peer_id: [u8; 20],+  sock: UdpSocket,+  state: State,+}++impl<'a> Client {+  const UDP_TRACKER_MAGIC: u64 = 0x0000_0417_2710_1980;++  pub fn new(peer_id: [u8; 20]) -> Result<Self> {+    Ok(Self {+      peer_id,+      sock: UdpSocket::bind("0.0.0.0:0").context(error::Network)?,+      state: State::Disconnected,+    })+  }++  pub fn connect(&mut self, host_port: &HostPort) -> Result<()> {+    let mut rng = rand::thread_rng();++    self.sock.connect(host_port).context(error::Network)?;++    let req = connect::Request {+      protocol_id: Self::UDP_TRACKER_MAGIC,+      action: 0x0000,+      transaction_id: rng.gen(),+    };++    let mut buf = [0u8; mem::size_of::<connect::Response>()];+    let (resp, _) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }++    self.state = State::Connected {+      id: resp.connection_id,+      epoch: Instant::now(),+      socket_addr: self.sock.peer_addr().context(error::Network)?,+      host_port: host_port.clone(),+    };++    Ok(())+  }++  pub fn announce(&self, btinh: Infohash) -> Result<Vec<SocketAddr>> {+    let mut rng = rand::thread_rng();+    let req = announce::Request {+      connection_id: self.state.get_connection_id()?,+      action: 0x0001,+      transaction_id: rng.gen(),+      infohash: btinh.into(),+      peer_id: self.peer_id,+      downloaded: 0x0000,+      left: u64::MAX,+      uploaded: 0x0000,+      event: 0x0000,+      ip_address: 0x0000,+      num_want: u32::MAX,+      port: self.sock.local_addr().context(error::Network)?.port(),+    };+    let mut buf = [0u8; 8192]; // is there a BUFSIZ macro for Rust?+    let (resp, len) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }+    self.parse_compact_peer_list(&buf[mem::size_of::<announce::Response>()..len])+  }++  fn roundtrip<T: Request>(&self, req: &T, rxbuf: &mut [u8]) -> Result<(T::Response, usize)> {

I think I like fn exchange a little better than roundtrip, round trip sounds to me like the same thing going in both directions.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use crate::common::*;++const INPUT_HELP: &str =+  "Read torrent metainfo from `INPUT`. If `INPUT` is `-`, read metainfo from standard input.";++const INPUT_FLAG: &str = "input-flag";++const INPUT_POSITIONAL: &str = "<INPUT>";++#[derive(StructOpt)]+#[structopt(+  help_message(consts::HELP_MESSAGE),+  version_message(consts::VERSION_MESSAGE),+  about("Announce a .torrent file.")+)]+pub(crate) struct Announce {+  #[structopt(+    name = INPUT_FLAG,+    long = "input",+    short = "i",+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    help = INPUT_HELP,+  )]+  input_flag: Option<InputTarget>,+  #[structopt(+    name = INPUT_POSITIONAL,+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    required_unless = INPUT_FLAG,+    conflicts_with = INPUT_FLAG,+    help = INPUT_HELP,+  )]+  input_positional: Option<InputTarget>,+}++impl Announce {+  pub(crate) fn run(self, env: &mut Env) -> Result<(), Error> {+    let target = xor_args(+      "input_flag",+      &self.input_flag,+      "input_positional",+      &self.input_positional,+    )?;+    let input = env.read(target)?;++    let mut rng = rand::thread_rng();+    let mut peer_list = Vec::new();+    let mut trackers = Vec::new();+    let infohash = Infohash::from_input(&input)?;+    let metainfo = Metainfo::from_input(&input)?;+    let peer_id: [u8; 20] = rng.gen();++    for result in metainfo.trackers() {+      match result {+        Ok(tracker) => trackers.push(tracker),+        Err(err) => errln!(env, "Failed to parse tracker URL: {}", err)?,+      }+    }++    if trackers.is_empty() {+      return Err(Error::MetainfoMissingTrackers);+    }++    let mut client = tracker::Client::new(peer_id)?;+    errln!(env, "[1/2] Announcing {} to trackers.", &infohash)?;

I don't think this needs a step list, since there's really only one step.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use super::*;+use crate::common::*;++pub(crate) struct Client {+  peer_id: [u8; 20],+  sock: UdpSocket,+  state: State,+}++impl<'a> Client {+  const UDP_TRACKER_MAGIC: u64 = 0x0000_0417_2710_1980;++  pub fn new(peer_id: [u8; 20]) -> Result<Self> {+    Ok(Self {+      peer_id,+      sock: UdpSocket::bind("0.0.0.0:0").context(error::Network)?,+      state: State::Disconnected,+    })+  }++  pub fn connect(&mut self, host_port: &HostPort) -> Result<()> {+    let mut rng = rand::thread_rng();++    self.sock.connect(host_port).context(error::Network)?;++    let req = connect::Request {+      protocol_id: Self::UDP_TRACKER_MAGIC,+      action: 0x0000,+      transaction_id: rng.gen(),+    };++    let mut buf = [0u8; mem::size_of::<connect::Response>()];+    let (resp, _) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }++    self.state = State::Connected {+      id: resp.connection_id,+      epoch: Instant::now(),+      socket_addr: self.sock.peer_addr().context(error::Network)?,+      host_port: host_port.clone(),+    };++    Ok(())+  }++  pub fn announce(&self, btinh: Infohash) -> Result<Vec<SocketAddr>> {+    let mut rng = rand::thread_rng();+    let req = announce::Request {+      connection_id: self.state.get_connection_id()?,+      action: 0x0001,+      transaction_id: rng.gen(),+      infohash: btinh.into(),+      peer_id: self.peer_id,+      downloaded: 0x0000,+      left: u64::MAX,+      uploaded: 0x0000,+      event: 0x0000,+      ip_address: 0x0000,+      num_want: u32::MAX,+      port: self.sock.local_addr().context(error::Network)?.port(),+    };+    let mut buf = [0u8; 8192]; // is there a BUFSIZ macro for Rust?+    let (resp, len) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {

Can these checks be done in roundtrip to avoid duplicating them?

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use crate::common::*;++const INPUT_HELP: &str =+  "Read torrent metainfo from `INPUT`. If `INPUT` is `-`, read metainfo from standard input.";++const INPUT_FLAG: &str = "input-flag";++const INPUT_POSITIONAL: &str = "<INPUT>";++#[derive(StructOpt)]+#[structopt(+  help_message(consts::HELP_MESSAGE),+  version_message(consts::VERSION_MESSAGE),+  about("Announce a .torrent file.")+)]+pub(crate) struct Announce {+  #[structopt(+    name = INPUT_FLAG,+    long = "input",+    short = "i",+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    help = INPUT_HELP,+  )]+  input_flag: Option<InputTarget>,+  #[structopt(+    name = INPUT_POSITIONAL,+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    required_unless = INPUT_FLAG,+    conflicts_with = INPUT_FLAG,+    help = INPUT_HELP,+  )]+  input_positional: Option<InputTarget>,+}++impl Announce {+  pub(crate) fn run(self, env: &mut Env) -> Result<(), Error> {+    let target = xor_args(+      "input_flag",+      &self.input_flag,+      "input_positional",+      &self.input_positional,+    )?;+    let input = env.read(target)?;++    let mut rng = rand::thread_rng();+    let mut peer_list = Vec::new();+    let mut trackers = Vec::new();+    let infohash = Infohash::from_input(&input)?;+    let metainfo = Metainfo::from_input(&input)?;+    let peer_id: [u8; 20] = rng.gen();++    for result in metainfo.trackers() {+      match result {+        Ok(tracker) => trackers.push(tracker),+        Err(err) => errln!(env, "Failed to parse tracker URL: {}", err)?,

"Skipping malformed tracker URL: {}"

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use super::*;+use crate::common::*;++pub(crate) struct Client {+  peer_id: [u8; 20],+  sock: UdpSocket,+  state: State,+}++impl<'a> Client {+  const UDP_TRACKER_MAGIC: u64 = 0x0000_0417_2710_1980;++  pub fn new(peer_id: [u8; 20]) -> Result<Self> {+    Ok(Self {+      peer_id,+      sock: UdpSocket::bind("0.0.0.0:0").context(error::Network)?,+      state: State::Disconnected,+    })+  }++  pub fn connect(&mut self, host_port: &HostPort) -> Result<()> {+    let mut rng = rand::thread_rng();++    self.sock.connect(host_port).context(error::Network)?;++    let req = connect::Request {+      protocol_id: Self::UDP_TRACKER_MAGIC,+      action: 0x0000,+      transaction_id: rng.gen(),+    };++    let mut buf = [0u8; mem::size_of::<connect::Response>()];+    let (resp, _) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }++    self.state = State::Connected {+      id: resp.connection_id,+      epoch: Instant::now(),+      socket_addr: self.sock.peer_addr().context(error::Network)?,+      host_port: host_port.clone(),+    };++    Ok(())+  }++  pub fn announce(&self, btinh: Infohash) -> Result<Vec<SocketAddr>> {+    let mut rng = rand::thread_rng();+    let req = announce::Request {+      connection_id: self.state.get_connection_id()?,+      action: 0x0001,+      transaction_id: rng.gen(),+      infohash: btinh.into(),+      peer_id: self.peer_id,+      downloaded: 0x0000,+      left: u64::MAX,+      uploaded: 0x0000,+      event: 0x0000,+      ip_address: 0x0000,+      num_want: u32::MAX,+      port: self.sock.local_addr().context(error::Network)?.port(),+    };+    let mut buf = [0u8; 8192]; // is there a BUFSIZ macro for Rust?+    let (resp, len) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }+    self.parse_compact_peer_list(&buf[mem::size_of::<announce::Response>()..len])+  }++  fn roundtrip<T: Request>(&self, req: &T, rxbuf: &mut [u8]) -> Result<(T::Response, usize)> {+    let msg = req.serialize();+    let read = self.send_and_retry_with_backoff(&msg, rxbuf)?;++    if read == 0 {+      Err(Error::TrackerTimeout)+    } else {+      T::Response::deserialize(&rxbuf[..read])+    }+  }++  fn send_and_retry_with_backoff(&self, txbuf: &'a [u8], rxbuf: &'a mut [u8]) -> Result<usize> {+    let mut len_read: usize = 0;++    for attempt in 0..=8 {+      self.sock.send(txbuf).context(error::Network)?;+      self+        .sock+        .set_read_timeout(Some(Duration::new(15 * 2u64.pow(attempt), 0)))+        .context(error::Network)?;++      if let Ok(len) = self.sock.recv(rxbuf) {+        len_read = len;+        break;+      } else {+        // Abort sending if the connection id goes stale during a back off+        if let State::Connected { epoch, .. } = self.state {+          if epoch.elapsed().as_secs() > 60 {+            return Err(Error::TrackerConnectionExpired);+          }+        }+      }+    }++    if len_read == 0 {+      Err(Error::TrackerTimeout)+    } else {+      Ok(len_read)+    }+  }++  // XXX: perhaps this should be in a different namespace

I think this is fine here.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use super::*;+use crate::common::*;++pub(crate) struct Client {+  peer_id: [u8; 20],+  sock: UdpSocket,+  state: State,+}++impl<'a> Client {+  const UDP_TRACKER_MAGIC: u64 = 0x0000_0417_2710_1980;++  pub fn new(peer_id: [u8; 20]) -> Result<Self> {+    Ok(Self {+      peer_id,+      sock: UdpSocket::bind("0.0.0.0:0").context(error::Network)?,+      state: State::Disconnected,+    })+  }++  pub fn connect(&mut self, host_port: &HostPort) -> Result<()> {+    let mut rng = rand::thread_rng();++    self.sock.connect(host_port).context(error::Network)?;++    let req = connect::Request {+      protocol_id: Self::UDP_TRACKER_MAGIC,+      action: 0x0000,+      transaction_id: rng.gen(),+    };++    let mut buf = [0u8; mem::size_of::<connect::Response>()];+    let (resp, _) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }++    self.state = State::Connected {+      id: resp.connection_id,+      epoch: Instant::now(),+      socket_addr: self.sock.peer_addr().context(error::Network)?,+      host_port: host_port.clone(),+    };++    Ok(())+  }++  pub fn announce(&self, btinh: Infohash) -> Result<Vec<SocketAddr>> {+    let mut rng = rand::thread_rng();+    let req = announce::Request {+      connection_id: self.state.get_connection_id()?,+      action: 0x0001,+      transaction_id: rng.gen(),+      infohash: btinh.into(),+      peer_id: self.peer_id,+      downloaded: 0x0000,+      left: u64::MAX,+      uploaded: 0x0000,+      event: 0x0000,+      ip_address: 0x0000,+      num_want: u32::MAX,+      port: self.sock.local_addr().context(error::Network)?.port(),+    };+    let mut buf = [0u8; 8192]; // is there a BUFSIZ macro for Rust?+    let (resp, len) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }+    self.parse_compact_peer_list(&buf[mem::size_of::<announce::Response>()..len])+  }++  fn roundtrip<T: Request>(&self, req: &T, rxbuf: &mut [u8]) -> Result<(T::Response, usize)> {+    let msg = req.serialize();+    let read = self.send_and_retry_with_backoff(&msg, rxbuf)?;++    if read == 0 {+      Err(Error::TrackerTimeout)+    } else {+      T::Response::deserialize(&rxbuf[..read])+    }+  }++  fn send_and_retry_with_backoff(&self, txbuf: &'a [u8], rxbuf: &'a mut [u8]) -> Result<usize> {+    let mut len_read: usize = 0;++    for attempt in 0..=8 {+      self.sock.send(txbuf).context(error::Network)?;+      self+        .sock+        .set_read_timeout(Some(Duration::new(15 * 2u64.pow(attempt), 0)))+        .context(error::Network)?;++      if let Ok(len) = self.sock.recv(rxbuf) {+        len_read = len;+        break;+      } else {+        // Abort sending if the connection id goes stale during a back off+        if let State::Connected { epoch, .. } = self.state {+          if epoch.elapsed().as_secs() > 60 {+            return Err(Error::TrackerConnectionExpired);+          }+        }+      }+    }++    if len_read == 0 {+      Err(Error::TrackerTimeout)+    } else {+      Ok(len_read)+    }+  }++  // XXX: perhaps this should be in a different namespace+  fn parse_compact_peer_list(&self, buf: &[u8]) -> Result<Vec<SocketAddr>> {+    let mut subswarm = Vec::<SocketAddr>::new();++    let stride = if self.state.is_ipv6() { 18 } else { 6 };++    // XXX: consider array_chunks()

Since array_chunks is nightly, and probably will be for a while, you can omit the note. And, actually, it wouldn't work here, since stride isn't a const.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

 pub(crate) enum Error {   FileSearch { source: ignore::Error },   #[snafu(display("Invalid glob: {}", source))]   GlobParse { source: globset::Error },+  #[snafu(display("Failed to parse host port"))]

Actually, the HostPartParseError isn't as detailed as I thought, so the original message is probably good:

#[snafu(display("Failed to parse host and port: {}", source))]

Since "host port" is not a common word, "host and port" is probably better.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use crate::common::*;++const INPUT_HELP: &str =+  "Read torrent metainfo from `INPUT`. If `INPUT` is `-`, read metainfo from standard input.";++const INPUT_FLAG: &str = "input-flag";++const INPUT_POSITIONAL: &str = "<INPUT>";++#[derive(StructOpt)]+#[structopt(+  help_message(consts::HELP_MESSAGE),+  version_message(consts::VERSION_MESSAGE),+  about("Announce a .torrent file.")+)]+pub(crate) struct Announce {+  #[structopt(+    name = INPUT_FLAG,+    long = "input",+    short = "i",+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    help = INPUT_HELP,+  )]+  input_flag: Option<InputTarget>,+  #[structopt(+    name = INPUT_POSITIONAL,+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    required_unless = INPUT_FLAG,+    conflicts_with = INPUT_FLAG,+    help = INPUT_HELP,+  )]+  input_positional: Option<InputTarget>,+}++impl Announce {+  pub(crate) fn run(self, env: &mut Env) -> Result<(), Error> {+    let target = xor_args(+      "input_flag",+      &self.input_flag,+      "input_positional",+      &self.input_positional,+    )?;+    let input = env.read(target)?;++    let mut rng = rand::thread_rng();+    let mut peer_list = Vec::new();+    let mut trackers = Vec::new();+    let infohash = Infohash::from_input(&input)?;+    let metainfo = Metainfo::from_input(&input)?;+    let peer_id: [u8; 20] = rng.gen();++    for result in metainfo.trackers() {+      match result {+        Ok(tracker) => trackers.push(tracker),+        Err(err) => errln!(env, "Failed to parse tracker URL: {}", err)?,+      }+    }++    if trackers.is_empty() {+      return Err(Error::MetainfoMissingTrackers);+    }++    let mut client = tracker::Client::new(peer_id)?;+    errln!(env, "[1/2] Announcing {} to trackers.", &infohash)?;++    for tracker in trackers {+      if tracker.scheme() == "udp" {+        let hostport = match HostPort::from_url(&tracker) {+          Some(hostport) => hostport,+          None => continue,

I think this deserves a message, otherwise users will be confused when trackers are skipped. "Skipping tracker URL without host and port: {}"

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use crate::common::*;++const INPUT_HELP: &str =+  "Read torrent metainfo from `INPUT`. If `INPUT` is `-`, read metainfo from standard input.";++const INPUT_FLAG: &str = "input-flag";++const INPUT_POSITIONAL: &str = "<INPUT>";++#[derive(StructOpt)]+#[structopt(+  help_message(consts::HELP_MESSAGE),+  version_message(consts::VERSION_MESSAGE),+  about("Announce a .torrent file.")+)]+pub(crate) struct Announce {+  #[structopt(+    name = INPUT_FLAG,+    long = "input",+    short = "i",+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    help = INPUT_HELP,+  )]+  input_flag: Option<InputTarget>,+  #[structopt(+    name = INPUT_POSITIONAL,+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    required_unless = INPUT_FLAG,+    conflicts_with = INPUT_FLAG,+    help = INPUT_HELP,+  )]+  input_positional: Option<InputTarget>,+}++impl Announce {+  pub(crate) fn run(self, env: &mut Env) -> Result<(), Error> {+    let target = xor_args(+      "input_flag",+      &self.input_flag,+      "input_positional",+      &self.input_positional,+    )?;+    let input = env.read(target)?;++    let mut rng = rand::thread_rng();+    let mut peer_list = Vec::new();+    let mut trackers = Vec::new();+    let infohash = Infohash::from_input(&input)?;+    let metainfo = Metainfo::from_input(&input)?;+    let peer_id: [u8; 20] = rng.gen();++    for result in metainfo.trackers() {+      match result {+        Ok(tracker) => trackers.push(tracker),+        Err(err) => errln!(env, "Failed to parse tracker URL: {}", err)?,+      }+    }++    if trackers.is_empty() {+      return Err(Error::MetainfoMissingTrackers);+    }++    let mut client = tracker::Client::new(peer_id)?;+    errln!(env, "[1/2] Announcing {} to trackers.", &infohash)?;++    for tracker in trackers {+      if tracker.scheme() == "udp" {+        let hostport = match HostPort::from_url(&tracker) {+          Some(hostport) => hostport,+          None => continue,+        };++        if let Err(err) = client.connect(&hostport) {+          errln!(env, "[...] Connection to {} failed: {}", hostport, err)?;

If there's no step list, then [...] can be removed.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use crate::common::*;++const INPUT_HELP: &str =+  "Read torrent metainfo from `INPUT`. If `INPUT` is `-`, read metainfo from standard input.";++const INPUT_FLAG: &str = "input-flag";++const INPUT_POSITIONAL: &str = "<INPUT>";++#[derive(StructOpt)]+#[structopt(+  help_message(consts::HELP_MESSAGE),+  version_message(consts::VERSION_MESSAGE),+  about("Announce a .torrent file.")+)]+pub(crate) struct Announce {+  #[structopt(+    name = INPUT_FLAG,+    long = "input",+    short = "i",+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    help = INPUT_HELP,+  )]+  input_flag: Option<InputTarget>,+  #[structopt(+    name = INPUT_POSITIONAL,+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    required_unless = INPUT_FLAG,+    conflicts_with = INPUT_FLAG,+    help = INPUT_HELP,+  )]+  input_positional: Option<InputTarget>,+}++impl Announce {+  pub(crate) fn run(self, env: &mut Env) -> Result<(), Error> {+    let target = xor_args(+      "input_flag",+      &self.input_flag,+      "input_positional",+      &self.input_positional,+    )?;+    let input = env.read(target)?;++    let mut rng = rand::thread_rng();+    let mut peer_list = Vec::new();+    let mut trackers = Vec::new();+    let infohash = Infohash::from_input(&input)?;+    let metainfo = Metainfo::from_input(&input)?;+    let peer_id: [u8; 20] = rng.gen();++    for result in metainfo.trackers() {+      match result {+        Ok(tracker) => trackers.push(tracker),+        Err(err) => errln!(env, "Failed to parse tracker URL: {}", err)?,+      }+    }++    if trackers.is_empty() {+      return Err(Error::MetainfoMissingTrackers);+    }++    let mut client = tracker::Client::new(peer_id)?;+    errln!(env, "[1/2] Announcing {} to trackers.", &infohash)?;++    for tracker in trackers {+      if tracker.scheme() == "udp" {

I would do:

if tracker.scheme() != "udp" {
   errln!(env, "Only UDP trackers are supported; skipping {}. tracker);
  continue
}

This keeps the check close to the message, and avoids a level of indentation.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

   clippy::map_unwrap_or,   clippy::missing_docs_in_private_items,   clippy::missing_inline_in_public_items,+  clippy::needless_lifetimes,

I forget, is this only needed on nightly?

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use super::*;+use crate::common::*;++pub(crate) struct Client {+  peer_id: [u8; 20],+  sock: UdpSocket,+  state: State,+}++impl<'a> Client {+  const UDP_TRACKER_MAGIC: u64 = 0x0000_0417_2710_1980;++  pub fn new(peer_id: [u8; 20]) -> Result<Self> {

I think I would just have the client generate a new, random PeerId instead of having it passed in.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

 pub(crate) enum Error {   SymlinkRoot { root: PathBuf },   #[snafu(display("Failed to retrieve system time: {}", source))]   SystemTime { source: SystemTimeError },+  #[snafu(display("The UDP tracker connection id has expired."))]+  TrackerConnectionExpired,+  #[snafu(display("UDP tracker has failed to respond 9 times."))]+  TrackerTimeout,+  #[snafu(display("UDP tracker response differed from expectations"))]

How about "Malformed response from UDP tracker"?

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use crate::common::*;++const INPUT_HELP: &str =+  "Read torrent metainfo from `INPUT`. If `INPUT` is `-`, read metainfo from standard input.";++const INPUT_FLAG: &str = "input-flag";++const INPUT_POSITIONAL: &str = "<INPUT>";++#[derive(StructOpt)]+#[structopt(+  help_message(consts::HELP_MESSAGE),+  version_message(consts::VERSION_MESSAGE),+  about("Announce a .torrent file.")+)]+pub(crate) struct Announce {+  #[structopt(+    name = INPUT_FLAG,+    long = "input",+    short = "i",+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    help = INPUT_HELP,+  )]+  input_flag: Option<InputTarget>,+  #[structopt(+    name = INPUT_POSITIONAL,+    value_name = "INPUT",+    empty_values(false),+    parse(try_from_os_str = InputTarget::try_from_os_str),+    required_unless = INPUT_FLAG,+    conflicts_with = INPUT_FLAG,+    help = INPUT_HELP,+  )]+  input_positional: Option<InputTarget>,+}++impl Announce {+  pub(crate) fn run(self, env: &mut Env) -> Result<(), Error> {+    let target = xor_args(+      "input_flag",+      &self.input_flag,+      "input_positional",+      &self.input_positional,+    )?;+    let input = env.read(target)?;++    let mut rng = rand::thread_rng();+    let mut peer_list = Vec::new();+    let mut trackers = Vec::new();+    let infohash = Infohash::from_input(&input)?;+    let metainfo = Metainfo::from_input(&input)?;+    let peer_id: [u8; 20] = rng.gen();++    for result in metainfo.trackers() {+      match result {+        Ok(tracker) => trackers.push(tracker),+        Err(err) => errln!(env, "Failed to parse tracker URL: {}", err)?,+      }+    }++    if trackers.is_empty() {+      return Err(Error::MetainfoMissingTrackers);+    }++    let mut client = tracker::Client::new(peer_id)?;

Did we have a strong reason for reusing the client between requests?

Looking at it now, it seems like it would be cleaner to make it immutable, and just re-create it on every request, in which case Client::new could be renamed Client::connect and do the actual connecting and return the connected client.

It seems like reusing the peer_id between trackers isn't ideal, and we could fold State into Client.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

 pub(crate) enum Error {   SymlinkRoot { root: PathBuf },   #[snafu(display("Failed to retrieve system time: {}", source))]   SystemTime { source: SystemTimeError },+  #[snafu(display("The UDP tracker connection id has expired."))]

Hypothetically, shouldn't this not be a user-facing error, since the client is supposed to get a new connection ID if the old one expires?

For simplicity, I propose we deviate from the spec a little bit for now, instead of worrying about handling getting a new connection ID. For every request, let's do 4 attempts every 15 seconds, and if we don't get a response by the last attempt, we give up and return a TrackerTimeout error. This keeps us under the 60 second limit for our connection ID, and later we can implement exponential backoff and re-requesting new connection IDs.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use super::*;+use crate::common::*;++pub(crate) struct Client {+  peer_id: [u8; 20],+  sock: UdpSocket,+  state: State,+}++impl<'a> Client {+  const UDP_TRACKER_MAGIC: u64 = 0x0000_0417_2710_1980;++  pub fn new(peer_id: [u8; 20]) -> Result<Self> {+    Ok(Self {+      peer_id,+      sock: UdpSocket::bind("0.0.0.0:0").context(error::Network)?,+      state: State::Disconnected,+    })+  }++  pub fn connect(&mut self, host_port: &HostPort) -> Result<()> {+    let mut rng = rand::thread_rng();++    self.sock.connect(host_port).context(error::Network)?;++    let req = connect::Request {+      protocol_id: Self::UDP_TRACKER_MAGIC,+      action: 0x0000,+      transaction_id: rng.gen(),+    };++    let mut buf = [0u8; mem::size_of::<connect::Response>()];+    let (resp, _) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }++    self.state = State::Connected {+      id: resp.connection_id,+      epoch: Instant::now(),+      socket_addr: self.sock.peer_addr().context(error::Network)?,+      host_port: host_port.clone(),+    };++    Ok(())+  }++  pub fn announce(&self, btinh: Infohash) -> Result<Vec<SocketAddr>> {+    let mut rng = rand::thread_rng();+    let req = announce::Request {+      connection_id: self.state.get_connection_id()?,+      action: 0x0001,+      transaction_id: rng.gen(),+      infohash: btinh.into(),+      peer_id: self.peer_id,+      downloaded: 0x0000,+      left: u64::MAX,+      uploaded: 0x0000,+      event: 0x0000,+      ip_address: 0x0000,+      num_want: u32::MAX,+      port: self.sock.local_addr().context(error::Network)?.port(),+    };+    let mut buf = [0u8; 8192]; // is there a BUFSIZ macro for Rust?

There actually is, you can use libc::BUFSIZ.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

+use super::*;+use crate::common::*;++pub(crate) struct Client {+  peer_id: [u8; 20],+  sock: UdpSocket,+  state: State,+}++impl<'a> Client {+  const UDP_TRACKER_MAGIC: u64 = 0x0000_0417_2710_1980;++  pub fn new(peer_id: [u8; 20]) -> Result<Self> {+    Ok(Self {+      peer_id,+      sock: UdpSocket::bind("0.0.0.0:0").context(error::Network)?,+      state: State::Disconnected,+    })+  }++  pub fn connect(&mut self, host_port: &HostPort) -> Result<()> {+    let mut rng = rand::thread_rng();++    self.sock.connect(host_port).context(error::Network)?;++    let req = connect::Request {+      protocol_id: Self::UDP_TRACKER_MAGIC,+      action: 0x0000,+      transaction_id: rng.gen(),+    };++    let mut buf = [0u8; mem::size_of::<connect::Response>()];+    let (resp, _) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }++    self.state = State::Connected {+      id: resp.connection_id,+      epoch: Instant::now(),+      socket_addr: self.sock.peer_addr().context(error::Network)?,+      host_port: host_port.clone(),+    };++    Ok(())+  }++  pub fn announce(&self, btinh: Infohash) -> Result<Vec<SocketAddr>> {+    let mut rng = rand::thread_rng();+    let req = announce::Request {+      connection_id: self.state.get_connection_id()?,+      action: 0x0001,+      transaction_id: rng.gen(),+      infohash: btinh.into(),+      peer_id: self.peer_id,+      downloaded: 0x0000,+      left: u64::MAX,+      uploaded: 0x0000,+      event: 0x0000,+      ip_address: 0x0000,+      num_want: u32::MAX,+      port: self.sock.local_addr().context(error::Network)?.port(),+    };+    let mut buf = [0u8; 8192]; // is there a BUFSIZ macro for Rust?+    let (resp, len) = self.roundtrip(&req, &mut buf)?;++    if resp.transaction_id != req.transaction_id || resp.action != req.action {+      return Err(Error::TrackerResponse);+    }+    self.parse_compact_peer_list(&buf[mem::size_of::<announce::Response>()..len])+  }++  fn roundtrip<T: Request>(&self, req: &T, rxbuf: &mut [u8]) -> Result<(T::Response, usize)> {+    let msg = req.serialize();+    let read = self.send_and_retry_with_backoff(&msg, rxbuf)?;++    if read == 0 {

I think this check is redundant with the one in send_and_retry_with_backoff and can be removed.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

 pub(crate) enum Error {   Internal { message: String },   #[snafu(display("Unknown lint: {}", text))]   LintUnknown { text: String },+  #[snafu(display("Failed to decode torrent metainfo from {}: {}", input, error))]+  MetainfoDecode {+    input: InputTarget,+    error: bendy::decoding::Error,+  },   #[snafu(display("Failed to deserialize torrent metainfo from {}: {}", input, source))]   MetainfoDeserialize {     source: bendy::serde::Error,     input: InputTarget,   },+  #[snafu(display("A peer source is required"))]+  MetainfoMissingTrackers,

How about "Torrent metainfo contains no trackers."? I think calling it "torrent metainfo" is likely to be more understandable to users, since they might not know what "metainfo" is.

atomgardner

comment created time in 4 days

Pull request review commentcasey/intermodal

Announce

 pub(crate) enum Error {   Internal { message: String },   #[snafu(display("Unknown lint: {}", text))]   LintUnknown { text: String },+  #[snafu(display("Failed to decode torrent metainfo from {}: {}", input, error))]+  MetainfoDecode {+    input: InputTarget,+    error: bendy::decoding::Error,+  },   #[snafu(display("Failed to deserialize torrent metainfo from {}: {}", input, source))]   MetainfoDeserialize {     source: bendy::serde::Error,     input: InputTarget,   },+  #[snafu(display("A peer source is required"))]+  MetainfoMissingTrackers,   #[snafu(display("Failed to serialize torrent metainfo: {}", source))]   MetainfoSerialize { source: bendy::serde::Error },-  #[snafu(display("Failed to decode torrent metainfo from {}: {}", input, error))]-  MetainfoDecode {-    input: InputTarget,-    error: bendy::decoding::Error,-  },   #[snafu(display("Metainfo from {} failed to validate: {}", input, source))]   MetainfoValidate {     input: InputTarget,     source: MetainfoError,   },+  #[snafu(display("Failed to interact with socket: {}", source))]+  Network { source: io::Error },

How about Network I/O error: {}?

atomgardner

comment created time in 4 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentcasey/intermodal

Announce

 pub(crate) enum Error {   FileSearch { source: ignore::Error },   #[snafu(display("Invalid glob: {}", source))]   GlobParse { source: globset::Error },+  #[snafu(display("Failed to parse host port"))]

This should print the source error, since those aren't automatically printed: #[snafu(display("{}", source))]

atomgardner

comment created time in 4 days

PullRequestReviewEvent

Pull request review commentcasey/intermodal

Announce

 examples:   text:    "BitTorrent metainfo related functionality is under the `torrent` subcommand:"   code:    "imdl torrent --help" +- command: imdl torrent announce

I suspect that the primary initial use for this command will be to get the printed peer list, so that should be mentioned. Like, "Announce a torrent to its trackers and print all peers returned:"

atomgardner

comment created time in 4 days

PullRequestReviewEvent

pull request commentcasey/intermodal

Add Void Linux package to README

It looks like the package was added in this commit:

https://github.com/void-linux/void-packages/commit/f93f68b79d26ae7555723013391a51a8f59bf417

Is that correct?

ThatNerdyPikachu

comment created time in 4 days

push eventcasey/local

Casey Rodarmor

commit sha 4b3222b884c8c9f41390f4a72e8d2268088c95dd

[zh] Update just completion script

view details

push time in 4 days

push eventcasey/local

Casey Rodarmor

commit sha 65bae27f8ed35241051b4a6bf501e684c94c7d82

[vim] Spellfile addition

view details

Casey Rodarmor

commit sha 33e57d2c8a4bb4981e376e5a573dcf2af3550945

[vim] Disable coc.nvim

view details

push time in 4 days

push eventcasey/bootstrap

Casey Rodarmor

commit sha 79a83a3038faf65b9df9e07430bc7bc6bce1cf2d

Fix readme spelling.

view details

Casey Rodarmor

commit sha 75263250e182944c2b0f5236dc23f8f2fdb27158

Bump version to v1.0.2

view details

push time in 4 days

issue closedcasey/just

Document Feature Flags

Sorry if it's already documented and I missed it, but from the Cargo.toml file I see there are feature flags for help4man2man and summary, but I don't see anywhere that explains what these do.

closed time in 5 days

LovecraftianHorror

issue commentcasey/just

Document Feature Flags

Good idea, they're not super useful, but it's good to document them nonetheless. I added comments to all the flags in Cargo.toml in #709.

LovecraftianHorror

comment created time in 5 days

push eventcasey/just

Casey Rodarmor

commit sha d7799ebec46753db26e098245f6fc14f7b093842

Document feature flags in Cargo.toml (#709) Just doesn't have any features that are likely to be useful to end users, but it's good to document them nonetheless.

view details

push time in 5 days

delete branch casey/just

delete branch : dff

delete time in 5 days

PR merged casey/just

Document feature flags in Cargo.toml
+11 -5

0 comment

1 changed file

casey

pr closed time in 5 days

PR opened casey/just

Document feature flags in Cargo.toml
+11 -5

0 comment

1 changed file

pr created time in 5 days

create barnchcasey/just

branch : dff

created branch time in 5 days

startedPSeitz/lz4_flex

started time in 5 days

pull request commentcasey/just

fix(zsh): Fix variable completion

Awesome, looks good!

heyrict

comment created time in 6 days

push eventcasey/just

Zhenhui Xie

commit sha 0e1af655650a785a016e17ccd63c77e035838438

Allow completing variables and recipes after `--set` in zsh completion script (#697)

view details

push time in 6 days

PR merged casey/just

fix(zsh): Fix variable completion

This PR contains the following fixes:

  • Allow completion of variables in the command context
  • Allow completion of variables after --set flag

Closes #695

+96 -10

15 comments

2 changed files

heyrict

pr closed time in 6 days

issue closedcasey/just

zsh autocomplete issue tabbing after passing --set VAR value

There is an issue with the zsh autocompletion when pressing tab after just --set VAR value It displays a block of error.

How to reproduce

Load autocompletion in your terminal

$ source <(just --completions=zsh)

Create a justfile

GREET_WORD := 'Hello'
greet name:
  @echo {{GREET_WORD}} {{name}}!

Use it

$ just greet Robert
Hello Robert!

Use it with autocompletion

If we want to set the variable GREET_WORD from the command line using --set, we would enter:
$ just --set GREET_WORD then press Tab
---> It autocompletes to:
$ just --set GREET_WORD greet
here, it should not autocomplete with a target, or anything, just wait for the user to enter a value.

But, if we type a correct content for the variable:
just --set GREET_WORD Hola then press Tab ----> we get:

$ just --set GREET_WORD Hola …error: The argument '--set <VARIABLE> <VALUE>' requires a value but none was supplied

USAGE:
    just --color <COLOR> --set <VARIABLE> <VALUE> --shell <SHELL> --shell-arg <SHELL-ARG>... <--choose|--completions <SHELL>|--dump|--edit|--init|--evaluate|--list|--show <RECIPE>|--summary|--variables>

For more information try --help
error: The argument '--set <VARIABLE> <VALUE>' requires a value but none was supplied

USAGE:
    just --color <COLOR> --set <VARIABLE> <VALUE> --shell <SHELL> --shell-arg <SHELL-ARG>... <--choose|--completions <SHELL>|--dump|--edit|--init|--evaluate|--list|--show <RECIPE>|--summary|--variables>

For more information try --help
error: The argument '--set <VARIABLE> <VALUE>' requires a value but none was supplied

USAGE:
    just --color <COLOR> --set <VARIABLE> <VALUE> --shell <SHELL> --shell-arg <SHELL-ARG>... <--choose|--completions <SHELL>|--dump|--edit|--init|--evaluate|--list|--show <RECIPE>|--summary|--variables>

For more information try --help

-> Here, it should have autocompleted with a target.

You can see the steps above in this recording: just-autocomplete-zsh-issue

Note

It would be nice to be able to set parameters with --set using space OR =, e.g.
just --set GREET_WORD Hola greet Robert
or
just --set GREET_WORD=Hola greet Robert
or
just --set=GREET_WORD=Hola greet Robert
or (with quotes, even): just --set="GREET_WORD=Hola" greet Robert (just mentioning this because using a = sign would not trigger this issue)

Versions

just v0.8.0 zsh 5.7.1 (x86_64-apple-darwin19.0)

closed time in 6 days

kenden

pull request commentcasey/just

fix(zsh): Fix variable completion

It looks like there's some state that's persisting between calls to the completion script.

$ just JUST_LOG=foo <TAB> # shows recipes and variables
$ just JUST_LOG=foo chang<TAB> # completes to `changes`
$ just JUST_LOG=foo <TAB> # now shows the text of the `changes recipe`
heyrict

comment created time in 6 days

pull request commentcasey/intermodal

Add FromStr for MagnetLink

Sweet, merged! Thank you!

atomgardner

comment created time in 6 days

PR merged casey/intermodal

Add FromStr for MagnetLink

Another step toward #255.

+240 -9

9 comments

8 changed files

atomgardner

pr closed time in 6 days

push eventcasey/intermodal

Thomas Gardner

commit sha 97ab785b7c229013217fc5c2534120b951caebe7

Implement FromStr for MagnetLink type: added

view details

push time in 6 days

PullRequestReviewEvent

push eventatomgardner/intermodal

Thomas Gardner

commit sha 97ab785b7c229013217fc5c2534120b951caebe7

Implement FromStr for MagnetLink type: added

view details

push time in 6 days

push eventatomgardner/intermodal

Thomas Gardner

commit sha d33fc51fc8aa2ff50b066a9de7741ab89a46d8b7

Implement `FromStr` for `MagnetLink` type: added

view details

push time in 6 days

pull request commentcasey/just

fix(zsh): Fix variable completion

I think this has an issue, if any flags or variables are given, recipe name tab completion doesn't work, and it instead shows the usage string for the first recipe:

$ just JUST_LOG=bar <TAB>
# add git log messages to changelog
changes:
    git log --pretty=format: >> CHANGELOG.md
heyrict

comment created time in 6 days

push eventheyrict/just

Casey Rodarmor

commit sha a1dec5d66751a346b6b0e93c223ff5c9cfc78eb7

Add FreeBSD port to readme (#705)

view details

Casey Rodarmor

commit sha b64718b295cb9288445c0e316392c15a26b660f2

Release v0.8.1 (#707) - Bump version: 0.8.0 → 0.8.1 - Update dependencies - Update changelog - Update man page - Update config test

view details

heyrict

commit sha ddc5d17715cf726154b612463948b146da4dac0a

fix(zsh): Fix variable completion

view details

heyrict

commit sha dda3f708bd0856798e63a45bb70872b7f5521aef

fix(zsh): Fix file pattern matching

view details

Casey Rodarmor

commit sha c3ebb9826d04d5105a77f3f336666a3b7bf27dc8

Update saved completion scripts

view details

Zhenhui Xie

commit sha c165b34680870cd89d35a2a7f186d67e03c6195d

fix: Ignore the second argument passed in --set

view details

Casey Rodarmor

commit sha 4eeabcfaf6e2ba0730ae93832ab213d41b0fb925

Remove trailing whitespace, update completion script

view details

Zhenhui Xie

commit sha 04b03cb11fb14946811ccdc7bbe88625e9ae7a96

fix: Find recipe by matching name

view details

Zhenhui Xie

commit sha 13a5d3ffe3fc36380cbf1c1b746bf1b683a529a8

chore: Update changes to just.zsh

view details

Zhenhui Xie

commit sha 8b02e30cd032df86251221a16c5d65f205b2c1a1

chore: Update just.zsh

view details

Casey Rodarmor

commit sha 39ab33a45d701c6fb70b0a2af3c59dc9059a7038

Update completion script

view details

push time in 6 days

push eventheyrict/just

Casey Rodarmor

commit sha 59a2278ba8f8a9659d0622bcc7e7d1117adf6baa

Update completion script

view details

push time in 6 days

pull request commentcasey/intermodal

Add FromStr for MagnetLink

I just realized a couple things that could be improved:

  1. There's an assert_matches macro that can be used to clean up the tests. You can do assert_matches!(VALUE, PATTERN if CONDITION).
atomgardner

comment created time in 6 days

pull request commentcasey/intermodal

Add FromStr for MagnetLink

Looks good! Can you flip the switch that lets me push to this branch? I need to push a version with my PGP signature attached in order to merge it.

atomgardner

comment created time in 6 days

push eventcasey/intermodal

Casey Rodarmor

commit sha 3323c31f1b0e88c54d344ccf3bf138db92160b77

Make everything pub so it shows up in docs

view details

Casey Rodarmor

commit sha f20f6dc07626e3b3ff577d6378fdb4859fb173e4

Require pub items to be documented.

view details

Casey Rodarmor

commit sha 67cff50299bd423700aa9160f4e608a4d7ff0f8a

Notes

view details

Casey Rodarmor

commit sha 2fc8e59ea994d3fae196f2af9b11b55cc8ca2b3d

Notes

view details

push time in 7 days

startedjoshua-maros/scones

started time in 8 days

pull request commentcasey/just

fix(zsh): Fix variable completion

This is definitely a good idea, but I don't think it can be implemented without undesirable side-effects with the argument parser, clap, that Just uses.

When I set the "value delimiter" for --set to =, this does work as you'd expect --set foo=bar. However, it will error if you do --set foo=bar=baz, i.e. if you use = in the value that you're trying to set. I think this would be undesirable, since it would mean that setting variables would fail if the value happened to contain a =.

heyrict

comment created time in 8 days

issue commentcasey/just

Recipes completion?

Awesome! Thanks to @vikesh-raj for contributing the improved completion script!

Nicolab

comment created time in 8 days

issue closedcasey/just

Recipes completion?

Hello,

First, thanks for this great project. It makes life more comfortable :+1:

I installed the completions script, it does the job for just command but not for recipes (like make). Normal or I forgot something?

closed time in 8 days

Nicolab

issue commentcasey/just

Recipes completion?

Ah, sorry, I was confused because although the completion script was improved to complete recipe names, it hadn't made it into a release.

I just cut v0.8.1, which has the improved completion script. It's been published to crates.io, and prebuilt-binaries are building on CI, and should be added to the tag soon.

Nicolab

comment created time in 8 days

created tagcasey/just

tagv0.8.1

🤖 Just a command runner

created time in 8 days

push eventcasey/just

Casey Rodarmor

commit sha b64718b295cb9288445c0e316392c15a26b660f2

Release v0.8.1 (#707) - Bump version: 0.8.0 → 0.8.1 - Update dependencies - Update changelog - Update man page - Update config test

view details

push time in 8 days

delete branch casey/just

delete branch : release

delete time in 8 days

PR merged casey/just

Release v0.8.1
  • Bump version: 0.8.0 → 0.8.1
  • Update dependencies
  • Update changelog
  • Update man page
  • Update config test
+194 -189

0 comment

5 changed files

casey

pr closed time in 8 days

PR opened casey/just

Release v0.8.1
  • Bump version: 0.8.0 → 0.8.1
  • Update dependencies
  • Update changelog
  • Update man page
  • Update config test
+194 -189

0 comment

5 changed files

pr created time in 8 days

create barnchcasey/just

branch : release

created branch time in 8 days

issue openedcasey/x

Warn that x gives users a lot of freedom

Ability to carefully choose layout is a double edged sword. Layout can be optimized for performance, but inflexible layouts will be difficult to evolve. Unless you're sure, stick to flexible enums and tables, and remember that flexible enums don't allow adding or removing variant fields, only variants. For maximum flexibility, make enum variants just contain a table.

created time in 8 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

 pub(crate) enum Error {   FileSearch { source: ignore::Error },   #[snafu(display("Invalid glob: {}", source))]   GlobParse { source: globset::Error },+  #[snafu(display("Failed to parse hex string `{}`: {}", text, source))]+  HexParse {

I'm also cool with putting it inside of the MagnetLinkParserError, I was thinking it be annoying.

atomgardner

comment created time in 9 days

PullRequestReviewEvent

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {+  #[snafu(display(+    "Peer address `{}` failed to parse into a (host:port): {}",+    text,+    source+  ))]+  Peer {+    text: String,+    source: HostPortParseError,+  },+  #[snafu(display("Magnet links must begin with `magnet:`"))]

I'm not wedded to the exact message, as long as it contains the invalid scheme. (Since I'm not sure if all users will know what a URL scheme is.)

atomgardner

comment created time in 9 days

PullRequestReviewEvent

pull request commentcasey/just

fix(zsh): Fix variable completion

I'm afraid not. Probably checking the first argument that matches the name of an existing recipe should work if you'd like to avoid the args checking hack. I'll try that once I have time.

That would be great. My expectation is that I'll forget to update this script when I add new command line options. If the completion script has fewer features, like not completing certain things that it could, I think that worst it if it doesn't get out of date.

heyrict

comment created time in 10 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

 impl Infohash {   } } +impl FromStr for Infohash {

Sorry, I should have been more specific. I meant that the code that parses an infohash from a hex string can be moved into the code that parses a magnet link from a string. So Infohash wouldn't implement FromStr.

atomgardner

comment created time in 10 days

PullRequestReviewEvent

issue commentcasey/just

Recipes completion?

Thanks for the kind words!

The scripts are auto-generated, so only some of them, which have been hand modified, auto-complete recipe names. I believe the ZSH, PowerShell, and Bash scripts have modifications to complete recipe names. Which shell are you using?

Nicolab

comment created time in 10 days

issue commentcasey/just

Please add that FreeBSD package exists to the "Packages" section in the README

Awesome! Added in #705.

I've run FreeBSD in the past, and have always really liked it, so I'm very happy that just is now easy to install there. Thank you very much for contributing the port!

yurivict

comment created time in 11 days

push eventcasey/just

Casey Rodarmor

commit sha a1dec5d66751a346b6b0e93c223ff5c9cfc78eb7

Add FreeBSD port to readme (#705)

view details

push time in 11 days

delete branch casey/just

delete branch : freebsd-port

delete time in 11 days

PR merged casey/just

Add information about FreeBSD port to readme

Fixes #704.

+2 -1

0 comment

1 changed file

casey

pr closed time in 11 days

issue closedcasey/just

Please add that FreeBSD package exists to the "Packages" section in the README

https://www.freshports.org/deskutils/just

closed time in 11 days

yurivict

push eventcasey/just

Casey Rodarmor

commit sha 72bcfbac2fe7153e3642086767663b6b22bbe797

Sort Linuxen by Name

view details

push time in 11 days

PR opened casey/just

Add information about FreeBSD port to readme
+1 -0

0 comment

1 changed file

pr created time in 11 days

create barnchcasey/just

branch : freebsd-port

created branch time in 11 days

push eventcasey/intermodal

Thomas Gardner

commit sha a787d6a964ede1e049f8861c7758c9d9c8a609f3

Update clippy restrictions type: changed

view details

Casey Rodarmor

commit sha 9e216f52e58427ddbf38533bba1232060f0d5ee2

Spec crate and beginnings of archive format - Create `imdl-spec` crate for holding specs - Create `imdl-spec/src/archive.rs` for archive spec

view details

push time in 11 days

issue openedcasey/x

Add ability to dump to text format for viewing messages

Could be YAML, JSON, or both. Needed for debugging.

created time in 11 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {+  #[snafu(display(+    "Peer address `{}` failed to parse into a (host:port): {}",+    text,+    source+  ))]+  Peer {+    text: String,+    source: HostPortParseError,+  },+  #[snafu(display("Magnet links must begin with `magnet:`"))]+  Scheme,+  #[snafu(display("Magnet link must have a topic that begins with `urn:btih`"))]+  Topic,

Topic -> TopicMissing

atomgardner

comment created time in 11 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {+  #[snafu(display(+    "Peer address `{}` failed to parse into a (host:port): {}",+    text,+    source+  ))]+  Peer {+    text: String,+    source: HostPortParseError,+  },+  #[snafu(display("Magnet links must begin with `magnet:`"))]+  Scheme,+  #[snafu(display("Magnet link must have a topic that begins with `urn:btih`"))]+  Topic,+  #[snafu(display("Magnet link must be a valid url: `{}`", source))]+  URL { source: url::ParseError },
Failed to parse url: `{}`
atomgardner

comment created time in 11 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {

Some of these messages will be redundant when printed in context, i.e. with Failed to parse magnet link: from the outer Error message.

atomgardner

comment created time in 11 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {

Tests should be added that use malformed magnet links to trigger all of these error variants, and check that their contents are correct.

atomgardner

comment created time in 11 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {+  #[snafu(display(+    "Peer address `{}` failed to parse into a (host:port): {}",+    text,+    source+  ))]+  Peer {+    text: String,+    source: HostPortParseError,+  },+  #[snafu(display("Magnet links must begin with `magnet:`"))]+  Scheme,+  #[snafu(display("Magnet link must have a topic that begins with `urn:btih`"))]

I think this is better with a colon:

"Magnet link must have a topic that begins with `urn:btih:`"
atomgardner

comment created time in 11 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {+  #[snafu(display(+    "Peer address `{}` failed to parse into a (host:port): {}",+    text,+    source+  ))]+  Peer {+    text: String,+    source: HostPortParseError,+  },+  #[snafu(display("Magnet links must begin with `magnet:`"))]
Invalid scheme `{}`, magnet links must use the `magnet:` scheme
atomgardner

comment created time in 11 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {+  #[snafu(display(+    "Peer address `{}` failed to parse into a (host:port): {}",+    text,+    source+  ))]+  Peer {

I would call this PeerAddress

atomgardner

comment created time in 11 days

Pull request review commentcasey/intermodal

Add FromStr for MagnetLink

+use crate::common::*;++#[derive(Debug, Snafu)]+#[snafu(visibility(pub(crate)))]+pub(crate) enum MagnetLinkParseError {+  #[snafu(display(+    "Peer address `{}` failed to parse into a (host:port): {}",

I think this can just be:

"Failed to parse peer address `{}`: {}", text, source
atomgardner

comment created time in 11 days

more