Minimal implementation of sys.inputs#2481
Minimal implementation of sys.inputs#2481edhebi wants to merge 1 commit intotypst:mainfrom edhebi:main
Conversation
mattfbacon
left a comment
There was a problem hiding this comment.
In regards to the Default::default comments, I think there could be two possible ways to improve the experience:
- Create a new function,
build_with_sys, or the inversebuild_without_sys(bikeshed names) - Use a builder pattern like
library::build().with_sys_args(sys_args).
|
|
||
| static LIBRARY: Lazy<Prehashed<Library>> = Lazy::new(|| { | ||
| let mut lib = typst_library::build(); | ||
| let mut lib = typst_library::build(Default::default()); |
There was a problem hiding this comment.
Clippy doesn't like Default::default. It wants you to include the actual type name.
There was a problem hiding this comment.
Really ? that's a bit silly. I didn't get that lint, did you do something special to get it ?
There was a problem hiding this comment.
There was a problem hiding this comment.
From that link, seems like it is restricted to when pedantic lints are enabled. Typst doesn't enable those on CI, so I'm not sure we should follow those.
With that said, changing to SysArguments::default() probably would aid readability anyway.
There was a problem hiding this comment.
probably would aid readability anyway
That's the idea of pedantic lints. The code does exactly the same thing, just a bit clearer because people may be confused about "the default of what?" when they see this.
| scope.category("sys"); | ||
| scope.define( | ||
| "version", | ||
| Version::from_iter([ | ||
| env!("CARGO_PKG_VERSION_MAJOR").parse::<u32>().unwrap(), | ||
| env!("CARGO_PKG_VERSION_MINOR").parse::<u32>().unwrap(), | ||
| env!("CARGO_PKG_VERSION_PATCH").parse::<u32>().unwrap(), | ||
| ]), | ||
| ); | ||
| scope.define( | ||
| "inputs", | ||
| Dict::from_iter(args.inputs.into_iter().map(|(k, v)| (k.into(), v))), | ||
| ); |
There was a problem hiding this comment.
This is a small point, but I have observed that you can get smaller and nicer output from rustfmt by doing something like
let inputs = Dict::from_iter(...);
scope.define("inputs", inputs);There was a problem hiding this comment.
I think it's fine this way, since scope.define("inputs", some_expression) is already kinda saying something like inputs = some_expression, so it's readable.
At most, I could see adding some comment like "// converting keys to EcoString" or something like that (?). Or the .into() could be changed to something like FinalType::from(k).
There was a problem hiding this comment.
It's not for clarity, just for formatting.
|
|
||
| Self { | ||
| library: Prehashed::new(typst_library::build()), | ||
| library: Prehashed::new(typst_library::build(Default::default())), |
There was a problem hiding this comment.
Ditto about Default::default
| } | ||
|
|
||
| let mut lib = typst_library::build(); | ||
| let mut lib = typst_library::build(Default::default()); |
There was a problem hiding this comment.
Ditto about Default::default
| let mut inputs = HashMap::with_capacity(command.plain_inputs.len()); | ||
| for (k, v) in &command.plain_inputs { | ||
| inputs.insert(k.clone().into(), Value::Str(v.clone().into())); | ||
| } | ||
| for raw in &command.json_inputs { | ||
| let root: HashMap<EcoString, Value> = | ||
| serde_json::from_str(raw).map_err(|e| eco_format!("{e}"))?; | ||
| inputs.reserve(root.len()); | ||
| for (key, val) in root { | ||
| inputs.insert(key, val); | ||
| } | ||
| } |
There was a problem hiding this comment.
This could be done more idiomatically with iterators.
There was a problem hiding this comment.
Iterators get kind of annoying here because from_iter requires annotations to deduce the RandomState type, but I'll try to see if I can find a nicer way
There was a problem hiding this comment.
That's strange. I'm surprised you can't just do .collect::<HashMap<EcoString, Value>>()
There was a problem hiding this comment.
That's strange. I'm surprised you can't just do
.collect::<HashMap<EcoString, Value>>()
I believe it should be possible (without using Dict::from_iter):
.collect() is a very powerful beast in Rust (see also a cool fun fact about .collect()).
There was a problem hiding this comment.
That collecting iterator of Results to Result of a container thing will be necessary here considering the possibility of failure for the JSON deserialization.
| let input = command.input_file.canonicalize().map_err(|_| { | ||
| eco_format!( | ||
| "input file not found (searched at {})", | ||
| command.input_file.display() |
| short = 'i', | ||
| value_name = "KEY=VALUE", | ||
| action = ArgAction::Append, | ||
| value_parser = ValueParser::new(parse_input_pair) |
| pub struct SysArguments { | ||
| /// A number of keyed inputs that can be provided by the platform and will appear | ||
| /// as `sys.inputs`. The main expected usecase is scripting. | ||
| pub inputs: HashMap<EcoString, Value>, |
There was a problem hiding this comment.
I think it could already be a Dict at this point.
|
|
||
| /// Hook up all sys definitions. | ||
| pub(super) fn define(global: &mut Scope, args: SysArguments) { | ||
| global.category("system"); |
There was a problem hiding this comment.
| global.category("system"); | |
| global.category("sys"); |
|
|
||
| /// Arguments for the `sys` module that handle implementation-specific behaviour. | ||
| #[derive(Clone, Default)] | ||
| pub struct SysArguments { |
There was a problem hiding this comment.
If we have this struct, maybe we could actually allow overriding the version here and keep the default as the Cargo.toml thing?
| value_name = "JSON", | ||
| action = ArgAction::Append, | ||
| )] | ||
| pub json_inputs: Vec<String>, |
There was a problem hiding this comment.
I'm not sure I like the json inputs thing. I'd like to keep the API surface minimal and using a single normal input in combination with json.parse can already achieve the same thing (and is not format-specific).
| // The key was missing or entirely blank | ||
| MissingKey, | ||
| // The value was missing or entirely blank | ||
| MissingValue, |
There was a problem hiding this comment.
Is passing the empty string not a valid thing?
| #[clap( | ||
| long = "input", | ||
| short = 'i', | ||
| value_name = "KEY=VALUE", |
There was a problem hiding this comment.
I'm not super familiar with typical command line argument conventions. Is this the canonical way to deal with multiple key-value inputs?
| #[derive(Debug, Clone, Args)] | ||
| pub struct SharedArgs { | ||
| /// Path to input Typst file | ||
| pub input: PathBuf, |
There was a problem hiding this comment.
the collision between the names here is a bit unfortunate ...
There was a problem hiding this comment.
Maybe "source" as the new name?
args.rs should not depend on typst because it's used in the build script. I think just |
|
@edhebi do you still plan on working on this? if not, someone else could pick it up since it's almost done. |
|
Closing in favor of #2894 due to inactivity. |
Fixes #295.
This isn't prod ready by any mean but I felt like I might as well push a minimal impl to gather some discussions.
Things of interest:
typst_library::build()which is fairly disruptive, but I feel it's ok given the fix is just addingDefault::default.sysmodule a bit special in typst_library as the arguments of build are explicitly named after it. I feel this is a good way to ensure going through the World stays the main way to handle platform-specific things, while sys becomes kind of its own thing.Keeping this as a draft for now, at least until the first wave of feedback