I have a basic struct like this
pub struct Args {
#[clap(short, long, value_parser)]
pub files: Vec<String>,
}
I'm trying to get this struct to take multiple values like such
cargo run -- --files hello world
But when I run this, it doesn't see world correctly. It errors with this:
error: Found argument 'world' which wasn't expected, or isn't valid in this context
What is the proper way to have clap populate this struct?
You can use num_args to specify a range for the number of argument occurences and values allowed like so:
use clap::Parser;
#[derive(Parser)]
pub struct Args {
#[clap(short, long, value_parser, num_args = 1.., value_delimiter = ' ')]
pub files: Vec<String>,
}
fn main() {
let args = Args::parse();
println!("files: {:?}", args.files);
}
This will allow both
cargo run -- --files hello world
and
cargo run -- --files hello --files world
Specifying the value_delimiter is not strictly neccessary here, but I just wanted to point out that you can use different characters as delimiters like this.
If you also want to allow empty arrays beeing passed, you can change the num_args attribute like so num_args = 0...
Related
const CELL_ALIGNMENT: usize = std::mem::align_of::<EntryType>();
#[repr(align(CELL_ALIGNMENT))]
pub struct AlignedCell;
#[repr(C)]
pub struct AlignedHeader {
_align: [AlignedCell; 0],
count: usize,
}
CELL_ALIGNMENT is a constant. But it looks like repr doesn't allow constant. Only literal is allowed. Is there any way to get around this?
No, you cannot use an expression in #[repr(align(_))]. It must be a literal.
The next best thing: you can assert the alignment of your struct matches another at compile-time by using the static-assertions crate:
#[repr(align(8))]
pub struct AlignedCell;
static_assertions::assert_eq_align!(AlignedCell, EntryType);
It doesn't set the alignment automatically, but you'll get a compiler error if its wrong.
Trying to use a struct within a struct in clap:
use clap::{Args, Parser};
use std::path::PathBuf;
#[derive(Parser, Debug)]
enum Command {
Foo(Foo),
}
#[derive(Args, Debug)]
struct Foo {
bar: Option<Bar>,
path: PathBuf,
}
#[derive(Parser, Clone, Debug)]
struct Bar {
bla: u8,
bla_2: String,
}
fn main() {
let cli = Command::parse();
println!("cli {:#?}", cli);
}
So I could call the app with the following options: cargo run -- foo bar 42 baz /tmp/a or just cargo run -- foo /tmp/a since the bar argument is optional.
However, currently it does not build:
--> src/main.rs:11:5
|
11 | bar: Option<Bar>,
| ^^^ the trait `FromStr` is not implemented for `Bar`
|
And since the values within Bar have to be space-separated implementing a FromStr would not do the trick anyway.
Is it even possible to do something of this fashion in clap currently?
There are several problems with your code. The biggest one is:
An optional positional item can never come before a required positional argument
This is a problem in your case because your command line looks like this:
cargo run -- <required> [optional] /tmp/a
If you have a required path at the end, there can not be an optional positional argument before that.
Further problems:
#[derive(Parser)] should be attached to a struct, not an enum.
There should only be one #[derive(Parser)], which represents the entry object of your arguments parser.
I'm unsure how else to help you, except pointing out your problems. If the invocations cargo run -- foo bar 42 baz /tmp/a and cargo run -- foo /tmp/a are non-negotiable, I don't think clap is the right library for you; I think you should parse by hand.
I have a macro that processes tokens into some nested structs.
I use these structs are a kind of "intermediate representation", that are then converted into output tokens by the macro.
I want to use these "intermediate representation" types at runtime as well though.
So if I have an instance of a Rust struct in a macro, how do I output that into the end program?
I could share the type between the macro crate and the output program crate, but to get the instance data would I have to do something like serialize to a JSON string, embed into the end program, and then parse the JSON string at runtime?
Thanks.
Simple example of a macro:
// This type will be used in both the macro and the program.
pub struct X {
a: String,
b: String
}
fn impl_x(ast: &syn::DeriveInput) -> TokenStream {
let x: X = ast.into();
let gen = quote! {
fn get_x() -> X {
// How to transfer instance `x` to the output TokenStream?
}
};
gen.into()
}
Note: I am looking for a general way to do this that works with large nested structs.
I'm currently trying to implement a shell built in Rust for practice, I've managed to implement most features, however, I'm currently trying to make a built-in command which takes path <arg1> <arg2> ..., and adds this to my program's path variable. However, I have this setup in that I have a loop which parses the command input and produces a command variable as a &str and its arguments as Vec<&str> which form my Command struct.
For this built-in command, path, I need to take the arguments and create a new vector of paths and replace the existing path. My current path variable is declared outside of my loop and is a Vec<&Path>. I'm currently receiving an error:
lifetime mismatch but data from `command` flows into `path` here
Which is understandable, but I've tried setting my args to have a 'static lifetime, but this failed as the borrowed value does not live long enough, as I read the line within the loop and replace it and process the string into my command struct. Would appreciate any help!
let line = stdin.lock().lines().next().unwrap().unwrap();
let split = line
.split_whitespace()
.enumerate()
.filter(|&(i, _)| i > 0)
.map(|(_, e)| e);
let args = split.collect::<Vec<&str>>();
// Command Struct
struct Program<'a> {
command: &'a str,
args: Vec<&'static str>,
}
// Dispatcher
fn dispatch_command<'a>(command: Program, path: &mut Vec<&Path>) -> Result<(), &'a str>
Rust beginner here.
I've been trying to learn the CSV crate but got stuck on the following case.
My goal is to:
Parse a nested array
Set column names to array values
Write to CSV
Firstly here is the code that outputs exactly what I want it to.
use serde::Serialize;
use serde::Deserialize;
use csv;
use serde_json;
use std::io;
#[derive(Debug,Serialize,Deserialize)]
#[serde(transparent)]
struct Parent {
arr_field: Vec<Row>
}
#[derive(Debug,Serialize,Deserialize)]
struct Row {
a: u8,
b: u8,
c: u8,
}
fn main() {
let resp = r#" [[1,2,3],[3,2,1],[4,5,6]] "#;
let mut wtr = csv::WriterBuilder::new().from_writer(io::stdout());
let v: Parent = serde_json::from_str(resp).unwrap();
for row in v.arr_field{
wtr.serialize(row);
}
}
The output of this code is:
a,b,c
1,2,3
3,2,1
4,5,6
But when I want to save the output to a local file rather than stdout, like so:
let mut wtr = csv::WriterBuilder::new().from_path("./foo.csv");
I'm getting the following error at wtr.serialize
error[E0599]: no method named `serialize` found for enum `std::result::Result<Writer<File>, csv::Error>` in the current scope
Thank you for your help.
The error message tells you all you need to know - from_path returns a Result rather than a WriterBuilder, because opening that file might not always work. That is different with from_writer - no file needs to be opened, so no possibility of encountering an error.
To fix this, you can just use .unwrap(), like you do with serde_json::from_str the line below. This will cause a panic when an error was encountered, immediately terminating your program.
let mut wtr = csv::WriterBuilder::new().from_path("./foo.csv").unwrap();
Note that serialize also returns a result, so you should also add .unwrap() or some other logic to handle errors in your for loop. Rust will likely show a warning that there is an unused result.