Command-line scripting with Rust. Wait, what?!

Command-line scripting with Rust. Wait, what?!

Transcript

  1. None
  2. @listochkin

  3. None
  4. @RustUkraine

  5. Rust

  6. Modern Language

  7. Markdown for Comments

  8. Build-in Unit testing

  9. Tests for code in the docs

  10. Package manager Cargo.toml Cargo.lock Multiple Versions

  11. Learning by Doing

  12. “System”

  13. Drop-in replacement for C

  14. Rust => .so / .dylib / .dll Node / Ruby

    / Python
  15. Drivers Desktop Software Mobile Apps Servers IoT

  16. Adoption: Project Infrastructure IDE / Editor integration CI / CD

  17. Add a few lines to a Dockerfile?

  18. One-off Scripts

  19. Ad-hoc data processing

  20. Troubleshooting

  21. Custom metrics / healthchecks

  22. Personal Workflows

  23. Bash Perl Ruby / Python Node

  24. find … | grep … | sed … | …

    > result.txt
  25. ssh + netcat + custom UNIX sockets

  26. Scripts for individual steps

  27. /regex/

  28. sed => perl

  29. small “programs”

  30. $EDITOR transform.lang chmod +x transform.lang ./transform.lang

  31. Rust?!

  32. cargo new omg-how-to-name-it cd omg-how-to-name-it cargo build ...

  33. Many files A new repository

  34. Not something temporary

  35. Not scripting

  36. cargo eval

  37. cargo install cargo-eval $EDITOR hello.rs

  38. #!/usr/bin/env cargo eval -- fn main() { println!("Hello world!"); }

  39. chmod +x hello.rs ./hello.rs Hello world!

  40. OMG!

  41. Rust Scripting

  42. None
  43. Cargo Eval supports Cargo modules

  44. #!/usr/bin/env cargo eval -- //! ```cargo //! [dependencies] //! time

    = "0.1.25" //! ``` extern crate time; fn main() { println!("{}", time::now().rfc822z()); }
  45. #!/usr/bin/env cargo eval -- //! ```cargo //! [package] //! edition

    = "2018" //! [dependencies] //! time = "0.1.25" //! ``` fn main() { println!("{}", time::now().rfc822z()); }
  46. #!/usr/bin/env cargo eval -- use std::io::*; fn main() -> Result<()>

    { for (n, line) in stdin().lock().lines().enumerate() { let line = line?; let line = line.trim_end(); if line.is_empty() { eprintln!("warning: empty line!"); } println!("{:>6}: {}", n, line.trim_end()) } Ok(()) }
  47. Text Processing

  48. str Strict UTF-8 not CESU-8 not Java Modified UTF-8

  49. str ⇔ String OsStr ⇔ OsString Path ⇔ PathBuf CStr

    ⇔ CString
  50. unicode_normalization unicode_segmentation

  51. let s = "a\u{30a}"; assert_eq!(s.nfc().collect::<String>(), "å");

  52. let s = "a̐éö̲\r\na\u{30a}"; let g: Vec<&str> = UnicodeSegmentation::graphemes(s, true)

    .collect(); let b = &["a̐", "é", "ö̲", "\r\n", "a\u{30a}"]; assert_eq!(g, b);
  53. Case Folding

  54. caseless

  55. assert_eq!( canonical_caseless_match_str("Office", "office"), true); assert_eq!( canonical_caseless_match_str("straße", "Strasse"), true);

  56. regex

  57. Extended Syntax Unicode Named Capture Groups

  58. No backreferences No conditionals No subroutines No recursion

  59. Sad :(

  60. lazy_static! { static ref RE: Regex = Regex::new( r"(?xu) #US

    date (?P<m> \d{2} ) # the month / (?P<d> \d{2} ) # the day / (?P<y> \d{4} ) # the year ", ) .unwrap(); } let before = "01/30/2018, 12/12/2019"; let after = RE.replace_all(before, "$y-$m-$d"); assert_eq!(after, "2018-01-30, 2019-12-12");
  61. pcre2 ?

  62. Running external commands

  63. duct

  64. use duct::cmd; cmd!( "osascript", "-e", "display notification \"Hello from Rust\"

    with title \"CLI\"" ) .run()?;
  65. Ergonomics

  66. quicli

  67. create_dir remove_dir_all glob read_file write_to_file

  68. CliResult logger CLI arguments Parallel Iterators

  69. None
  70. Drawbacks

  71. So many crates!

  72. Rust Regexes :/

  73. Extra care is needed

  74. What’s good?

  75. rustfmt ./your-script.rs

  76. Compiler helps

  77. Doesn’t have to deal with Traits Borrow Checker

  78. Compile most deps once

  79. If you script often compilation time isn’t an issue

  80. Rust Scripting is real

  81. … | rust | …

  82. None