boot / ⠛⠕⠞⠞⠁⠀⠃⠕⠕⠞⠀⠮⠀⠍⠑⠞⠁⠍⠁⠡⠔⠑
📦 Move files out of the way
✯ Audio reactive visuals engine in C++/OpenGL
💀 Do or die: quick and dirty utility functions in rust
Tulip Farm Discord bot
casey/9x 1
Plan 9 for Unix
Rich, structured content metadata for BitTorrent
Type-safe, compiled Jinja-like templates for Rust
Invoke `x.py` slightly more conveniently
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.
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!
comment created time in 3 days
PR merged casey/intermodal
I'll undraft this once https://github.com/void-linux/void-packages/pull/24520 is merged.
pr closed time in 3 days
push eventcasey/intermodal
commit sha 05ba87039b6bd0044a4c6f3e19afc0843022e4a6
Add Void Linux package to README type: documentation
push time in 3 days
push eventThatNerdyPikachu/intermodal
commit sha 05ba87039b6bd0044a4c6f3e19afc0843022e4a6
Add Void Linux package to README type: documentation
push time in 3 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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?
comment created time in 4 days
Pull request review commentcasey/intermodal
+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: {}"
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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: {}"
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
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?
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
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"?
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
+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.
comment created time in 4 days
Pull request review commentcasey/intermodal
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.
comment created time in 4 days
Pull request review commentcasey/intermodal
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: {}?
comment created time in 4 days
Pull request review commentcasey/intermodal
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))]
comment created time in 4 days
Pull request review commentcasey/intermodal
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:"
comment created time in 4 days
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?
comment created time in 4 days
push eventcasey/local
commit sha 4b3222b884c8c9f41390f4a72e8d2268088c95dd
[zh] Update just completion script
push time in 4 days
push eventcasey/local
commit sha 65bae27f8ed35241051b4a6bf501e684c94c7d82
[vim] Spellfile addition
commit sha 33e57d2c8a4bb4981e376e5a573dcf2af3550945
[vim] Disable coc.nvim
push time in 4 days
push eventcasey/bootstrap
commit sha 79a83a3038faf65b9df9e07430bc7bc6bce1cf2d
Fix readme spelling.
commit sha 75263250e182944c2b0f5236dc23f8f2fdb27158
Bump version to v1.0.2
push time in 4 days
issue closedcasey/just
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
LovecraftianHorrorissue commentcasey/just
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.
comment created time in 5 days
push eventcasey/just
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.
push time in 5 days
PR merged casey/just
pr closed time in 5 days
PR opened casey/just
pr created time in 5 days
startedPSeitz/lz4_flex
started time in 5 days
pull request commentcasey/just
fix(zsh): Fix variable completion
Awesome, looks good!
comment created time in 6 days
push eventcasey/just
commit sha 0e1af655650a785a016e17ccd63c77e035838438
Allow completing variables and recipes after `--set` in zsh completion script (#697)
push time in 6 days
PR merged casey/just
This PR contains the following fixes:
- Allow completion of variables in the command context
- Allow completion of variables after
--setflag
Closes #695
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:

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
kendenpull 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`
comment created time in 6 days
pull request commentcasey/intermodal
Sweet, merged! Thank you!
comment created time in 6 days
push eventcasey/intermodal
commit sha 97ab785b7c229013217fc5c2534120b951caebe7
Implement FromStr for MagnetLink type: added
push time in 6 days
push eventatomgardner/intermodal
commit sha 97ab785b7c229013217fc5c2534120b951caebe7
Implement FromStr for MagnetLink type: added
push time in 6 days
push eventatomgardner/intermodal
commit sha d33fc51fc8aa2ff50b066a9de7741ab89a46d8b7
Implement `FromStr` for `MagnetLink` type: added
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
comment created time in 6 days
push eventheyrict/just
commit sha a1dec5d66751a346b6b0e93c223ff5c9cfc78eb7
Add FreeBSD port to readme (#705)
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
commit sha ddc5d17715cf726154b612463948b146da4dac0a
fix(zsh): Fix variable completion
commit sha dda3f708bd0856798e63a45bb70872b7f5521aef
fix(zsh): Fix file pattern matching
commit sha c3ebb9826d04d5105a77f3f336666a3b7bf27dc8
Update saved completion scripts
commit sha c165b34680870cd89d35a2a7f186d67e03c6195d
fix: Ignore the second argument passed in --set
commit sha 4eeabcfaf6e2ba0730ae93832ab213d41b0fb925
Remove trailing whitespace, update completion script
commit sha 04b03cb11fb14946811ccdc7bbe88625e9ae7a96
fix: Find recipe by matching name
commit sha 13a5d3ffe3fc36380cbf1c1b746bf1b683a529a8
chore: Update changes to just.zsh
commit sha 8b02e30cd032df86251221a16c5d65f205b2c1a1
chore: Update just.zsh
commit sha 39ab33a45d701c6fb70b0a2af3c59dc9059a7038
Update completion script
push time in 6 days
push eventheyrict/just
commit sha 59a2278ba8f8a9659d0622bcc7e7d1117adf6baa
Update completion script
push time in 6 days
pull request commentcasey/intermodal
I just realized a couple things that could be improved:
- There's an
assert_matchesmacro that can be used to clean up the tests. You can doassert_matches!(VALUE, PATTERN if CONDITION).
comment created time in 6 days
pull request commentcasey/intermodal
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.
comment created time in 6 days
push eventcasey/intermodal
commit sha 3323c31f1b0e88c54d344ccf3bf138db92160b77
Make everything pub so it shows up in docs
commit sha f20f6dc07626e3b3ff577d6378fdb4859fb173e4
Require pub items to be documented.
commit sha 67cff50299bd423700aa9160f4e608a4d7ff0f8a
Notes
commit sha 2fc8e59ea994d3fae196f2af9b11b55cc8ca2b3d
Notes
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 =.
comment created time in 8 days
issue commentcasey/just
Awesome! Thanks to @vikesh-raj for contributing the improved completion script!
comment created time in 8 days
issue closedcasey/just
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
Nicolabissue commentcasey/just
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.
comment created time in 8 days
push eventcasey/just
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
push time in 8 days
PR merged casey/just
- Bump version: 0.8.0 → 0.8.1
- Update dependencies
- Update changelog
- Update man page
- Update config test
pr closed time in 8 days
PR opened casey/just
- Bump version: 0.8.0 → 0.8.1
- Update dependencies
- Update changelog
- Update man page
- Update config test
pr created 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
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.
comment created time in 9 days
Pull request review commentcasey/intermodal
+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.)
comment created time in 9 days
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.
comment created time in 10 days
Pull request review commentcasey/intermodal
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.
comment created time in 10 days
issue commentcasey/just
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?
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!
comment created time in 11 days
push eventcasey/just
commit sha a1dec5d66751a346b6b0e93c223ff5c9cfc78eb7
Add FreeBSD port to readme (#705)
push time in 11 days
PR merged casey/just
Fixes #704.
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
yurivictpush eventcasey/just
commit sha 72bcfbac2fe7153e3642086767663b6b22bbe797
Sort Linuxen by Name
push time in 11 days
PR opened casey/just
pr created time in 11 days
push eventcasey/intermodal
commit sha a787d6a964ede1e049f8861c7758c9d9c8a609f3
Update clippy restrictions type: changed
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
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
+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
comment created time in 11 days
Pull request review commentcasey/intermodal
+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: `{}`
comment created time in 11 days
Pull request review commentcasey/intermodal
+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.
comment created time in 11 days
Pull request review commentcasey/intermodal
+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.
comment created time in 11 days
Pull request review commentcasey/intermodal
+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:`"
comment created time in 11 days
Pull request review commentcasey/intermodal
+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
comment created time in 11 days
Pull request review commentcasey/intermodal
+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
comment created time in 11 days
Pull request review commentcasey/intermodal
+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
comment created time in 11 days