difftreelog
chore(slop) cargo metadata
in: master
17 files changed
cmds/jrsonnet-deps/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet-deps/Cargo.toml
+++ b/cmds/jrsonnet-deps/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-deps"
-description = "List dependencies of a jsonnet file"
+description = "Command-line tool that lists imports of a jsonnet file"
+keywords = ["jsonnet", "dependencies", "cli"]
+categories = ["command-line-utilities"]
authors.workspace = true
edition.workspace = true
license.workspace = true
cmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet-fmt/Cargo.toml
+++ b/cmds/jrsonnet-fmt/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-fmt"
-description = "Jrsonnet code formatter CLI"
+description = "Command-line jsonnet code formatter"
+keywords = ["jsonnet", "formatter", "cli"]
+categories = ["command-line-utilities", "development-tools"]
authors.workspace = true
edition.workspace = true
license.workspace = true
cmds/jrsonnet/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet"
-description = "Rust jsonnet implementation"
+description = "Rust implementation of the jsonnet command-line interpreter"
+keywords = ["jsonnet", "json", "config", "cli"]
+categories = ["command-line-utilities", "config", "template-engine"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-cli/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-cli/Cargo.toml
+++ b/crates/jrsonnet-cli/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-cli"
-description = "Utilities for building jrsonnet CLIs"
+description = "Reusable clap-based building blocks for jrsonnet command-line tools"
+keywords = ["jsonnet", "cli", "clap"]
+categories = ["command-line-interface"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -1,6 +1,9 @@
[package]
name = "jrsonnet-evaluator"
-description = "jsonnet interpreter"
+description = "Rust implementation of the jsonnet language interpreter"
+keywords = ["jsonnet", "interpreter", "config"]
+categories = ["compilers", "template-engine", "config"]
+readme = "README.adoc"
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth1use std::{2 any::Any,3 borrow::Cow,4 env::current_dir,5 fmt, fs,6 io::{ErrorKind, Read},7 path::{Path, PathBuf},8};910use fs::File;11use jrsonnet_gcmodule::Acyclic;12use jrsonnet_interner::IBytes;13use jrsonnet_ir::{14 IStr, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,15};1617use crate::{18 bail,19 error::{ErrorKind::*, Result},20};21#[derive(Clone, Debug, Acyclic, Eq, Hash, PartialEq)]22pub enum ResolvePathOwned {23 Str(String),24 Path(PathBuf),25}26impl fmt::Display for ResolvePathOwned {27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {28 match self {29 ResolvePathOwned::Str(s) => write!(f, "{s}"),30 ResolvePathOwned::Path(p) => write!(f, "{}", p.display()),31 }32 }33}34#[derive(Clone, Copy)]35pub enum ResolvePath<'s> {36 Str(&'s str),37 Path(&'s Path),38}39impl ResolvePath<'_> {40 pub fn to_owned(self) -> ResolvePathOwned {41 match self {42 ResolvePath::Str(s) => ResolvePathOwned::Str(s.to_owned()),43 ResolvePath::Path(p) => ResolvePathOwned::Path(p.to_owned()),44 }45 }46}47impl AsRef<Path> for ResolvePath<'_> {48 fn as_ref(&self) -> &Path {49 match self {50 ResolvePath::Str(s) => s.as_ref(),51 ResolvePath::Path(p) => p,52 }53 }54}55pub trait AsPathLike {56 fn as_path(&self) -> ResolvePath<'_>;57}58impl<T> AsPathLike for &T59where60 T: AsPathLike + ?Sized,61{62 fn as_path(&self) -> ResolvePath<'_> {63 (*self).as_path()64 }65}66impl AsPathLike for str {67 fn as_path(&self) -> ResolvePath<'_> {68 ResolvePath::Str(self)69 }70}71impl AsPathLike for IStr {72 fn as_path(&self) -> ResolvePath<'_> {73 ResolvePath::Str(self)74 }75}76impl AsPathLike for Cow<'_, Path> {77 fn as_path(&self) -> ResolvePath<'_> {78 ResolvePath::Path(self.as_ref())79 }80}81impl AsPathLike for Path {82 fn as_path(&self) -> ResolvePath<'_> {83 ResolvePath::Path(self)84 }85}86impl AsPathLike for ResolvePathOwned {87 fn as_path(&self) -> ResolvePath<'_> {88 match self {89 ResolvePathOwned::Str(s) => ResolvePath::Str(s),90 ResolvePathOwned::Path(path_buf) => ResolvePath::Path(path_buf),91 }92 }93}9495/// Implements file resolution logic for `import` and `importStr`96pub trait ImportResolver: Acyclic + Any {97 /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond98 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`99 /// where `${vendor}` is a library path.100 ///101 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value102 /// may result in panic103 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {104 bail!(ImportNotSupported(from.clone(), path.as_path().to_owned()))105 }106 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {107 self.resolve_from(&SourcePath::default(), path)108 }109110 /// Load resolved file111 /// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],112 /// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]113 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;114}115116/// Dummy resolver, can't resolve/load any file117#[derive(Acyclic)]118pub struct DummyImportResolver;119impl ImportResolver for DummyImportResolver {120 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {121 panic!("dummy resolver can't load any file")122 }123}124#[allow(clippy::use_self)]125impl Default for Box<dyn ImportResolver> {126 fn default() -> Self {127 Box::new(DummyImportResolver)128 }129}130131/// File resolver, can load file from both FS and library paths132#[derive(Default, Acyclic)]133pub struct FileImportResolver {134 /// Library directories to search for file.135 /// Referred to as `jpath` in original jsonnet implementation.136 library_paths: Vec<PathBuf>,137}138impl FileImportResolver {139 pub fn new(library_paths: Vec<PathBuf>) -> Self {140 Self { library_paths }141 }142 /// Dynamically add new jpath, used by bindings143 pub fn add_jpath(&mut self, path: PathBuf) {144 self.library_paths.push(path);145 }146}147148/// Create `SourcePath` from path, handling directories/Fifo files (on unix)/etc149fn check_path(path: &Path) -> Result<Option<SourcePath>> {150 let meta = match fs::metadata(path) {151 Ok(v) => v,152 Err(e) if e.kind() == ErrorKind::NotFound => {153 return Ok(None);154 }155 Err(e) => bail!(ImportIo(e.to_string())),156 };157 let ty = meta.file_type();158 if ty.is_file() {159 return Ok(Some(SourcePath::new(SourceFile::new(160 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,161 ))));162 }163 let ty = meta.file_type();164 #[cfg(unix)]165 {166 use std::os::unix::fs::FileTypeExt;167 if ty.is_fifo() {168 let file = fs::read(path).map_err(|e| ImportIo(format!("FIFO read failed: {e}")))?;169 return Ok(Some(SourcePath::new(SourceFifo(170 format!("{}", path.display()),171 IBytes::from(file.as_slice()),172 ))));173 }174 }175 // Block device/some other magic thing.176 Err(RuntimeError("special file can't be imported".into()).into())177}178179impl ImportResolver for FileImportResolver {180 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {181 let path = path.as_path();182 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {183 let mut o = f.path().to_owned();184 o.pop();185 o186 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {187 d.path().to_owned()188 } else if from.downcast_ref::<SourceDefaultIgnoreJpath>().is_some() {189 let mut direct = current_dir().map_err(|e| ImportIo(e.to_string()))?;190 direct.push(path);191 if let Some(direct) = check_path(&direct)? {192 return Ok(direct);193 }194 bail!(ImportFileNotFound(from.clone(), path.to_owned()))195 } else if from.is_default() {196 current_dir().map_err(|e| ImportIo(e.to_string()))?197 } else {198 unreachable!("resolver can't return this path")199 };200201 direct.push(path);202 if let Some(direct) = check_path(&direct)? {203 return Ok(direct);204 }205 for library_path in &self.library_paths {206 let mut cloned = library_path.clone();207 cloned.push(path);208 if let Some(cloned) = check_path(&cloned)? {209 return Ok(cloned);210 }211 }212 bail!(ImportFileNotFound(from.clone(), path.to_owned()))213 }214215 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {216 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {217 f.path()218 } else if id.downcast_ref::<SourceDirectory>().is_some() {219 bail!(ImportIsADirectory(id.clone()))220 } else if let Some(f) = id.downcast_ref::<SourceFifo>() {221 return Ok(f.1.to_vec());222 } else {223 unreachable!("other types are not supported in resolve");224 };225 let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;226 let mut out = Vec::new();227 file.read_to_end(&mut out)228 .map_err(|e| ImportIo(e.to_string()))?;229 Ok(out)230 }231232 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {233 self.resolve_from(&SourcePath::default(), path)234 }235}1use std::{2 any::Any,3 borrow::Cow,4 env::current_dir,5 fmt, fs,6 io::{ErrorKind, Read},7 path::{Path, PathBuf},8};910use fs::File;11use jrsonnet_gcmodule::Acyclic;12use jrsonnet_ir::{13 IStr, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,14};1516use crate::{17 bail,18 error::{ErrorKind::*, Result},19};20#[derive(Clone, Debug, Acyclic, Eq, Hash, PartialEq)]21pub enum ResolvePathOwned {22 Str(String),23 Path(PathBuf),24}25impl fmt::Display for ResolvePathOwned {26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {27 match self {28 ResolvePathOwned::Str(s) => write!(f, "{s}"),29 ResolvePathOwned::Path(p) => write!(f, "{}", p.display()),30 }31 }32}33#[derive(Clone, Copy)]34pub enum ResolvePath<'s> {35 Str(&'s str),36 Path(&'s Path),37}38impl ResolvePath<'_> {39 pub fn to_owned(self) -> ResolvePathOwned {40 match self {41 ResolvePath::Str(s) => ResolvePathOwned::Str(s.to_owned()),42 ResolvePath::Path(p) => ResolvePathOwned::Path(p.to_owned()),43 }44 }45}46impl AsRef<Path> for ResolvePath<'_> {47 fn as_ref(&self) -> &Path {48 match self {49 ResolvePath::Str(s) => s.as_ref(),50 ResolvePath::Path(p) => p,51 }52 }53}54pub trait AsPathLike {55 fn as_path(&self) -> ResolvePath<'_>;56}57impl<T> AsPathLike for &T58where59 T: AsPathLike + ?Sized,60{61 fn as_path(&self) -> ResolvePath<'_> {62 (*self).as_path()63 }64}65impl AsPathLike for str {66 fn as_path(&self) -> ResolvePath<'_> {67 ResolvePath::Str(self)68 }69}70impl AsPathLike for IStr {71 fn as_path(&self) -> ResolvePath<'_> {72 ResolvePath::Str(self)73 }74}75impl AsPathLike for Cow<'_, Path> {76 fn as_path(&self) -> ResolvePath<'_> {77 ResolvePath::Path(self.as_ref())78 }79}80impl AsPathLike for Path {81 fn as_path(&self) -> ResolvePath<'_> {82 ResolvePath::Path(self)83 }84}85impl AsPathLike for ResolvePathOwned {86 fn as_path(&self) -> ResolvePath<'_> {87 match self {88 ResolvePathOwned::Str(s) => ResolvePath::Str(s),89 ResolvePathOwned::Path(path_buf) => ResolvePath::Path(path_buf),90 }91 }92}9394/// Implements file resolution logic for `import` and `importStr`95pub trait ImportResolver: Acyclic + Any {96 /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond97 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`98 /// where `${vendor}` is a library path.99 ///100 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value101 /// may result in panic102 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {103 bail!(ImportNotSupported(from.clone(), path.as_path().to_owned()))104 }105 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {106 self.resolve_from(&SourcePath::default(), path)107 }108109 /// Load resolved file110 /// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],111 /// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]112 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;113}114115/// Dummy resolver, can't resolve/load any file116#[derive(Acyclic)]117pub struct DummyImportResolver;118impl ImportResolver for DummyImportResolver {119 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {120 panic!("dummy resolver can't load any file")121 }122}123#[allow(clippy::use_self)]124impl Default for Box<dyn ImportResolver> {125 fn default() -> Self {126 Box::new(DummyImportResolver)127 }128}129130/// File resolver, can load file from both FS and library paths131#[derive(Default, Acyclic)]132pub struct FileImportResolver {133 /// Library directories to search for file.134 /// Referred to as `jpath` in original jsonnet implementation.135 library_paths: Vec<PathBuf>,136}137impl FileImportResolver {138 pub fn new(library_paths: Vec<PathBuf>) -> Self {139 Self { library_paths }140 }141 /// Dynamically add new jpath, used by bindings142 pub fn add_jpath(&mut self, path: PathBuf) {143 self.library_paths.push(path);144 }145}146147/// Create `SourcePath` from path, handling directories/Fifo files (on unix)/etc148fn check_path(path: &Path) -> Result<Option<SourcePath>> {149 let meta = match fs::metadata(path) {150 Ok(v) => v,151 Err(e) if e.kind() == ErrorKind::NotFound => {152 return Ok(None);153 }154 Err(e) => bail!(ImportIo(e.to_string())),155 };156 let ty = meta.file_type();157 if ty.is_file() {158 return Ok(Some(SourcePath::new(SourceFile::new(159 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,160 ))));161 }162 #[cfg(unix)]163 {164 use std::os::unix::fs::FileTypeExt;165166 use jrsonnet_interner::IBytes;167168 let ty = meta.file_type();169170 if ty.is_fifo() {171 let file = fs::read(path).map_err(|e| ImportIo(format!("FIFO read failed: {e}")))?;172 return Ok(Some(SourcePath::new(SourceFifo(173 format!("{}", path.display()),174 IBytes::from(file.as_slice()),175 ))));176 }177 }178 // Block device/some other magic thing.179 Err(RuntimeError("special file can't be imported".into()).into())180}181182impl ImportResolver for FileImportResolver {183 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {184 let path = path.as_path();185 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {186 let mut o = f.path().to_owned();187 o.pop();188 o189 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {190 d.path().to_owned()191 } else if from.downcast_ref::<SourceDefaultIgnoreJpath>().is_some() {192 let mut direct = current_dir().map_err(|e| ImportIo(e.to_string()))?;193 direct.push(path);194 if let Some(direct) = check_path(&direct)? {195 return Ok(direct);196 }197 bail!(ImportFileNotFound(from.clone(), path.to_owned()))198 } else if from.is_default() {199 current_dir().map_err(|e| ImportIo(e.to_string()))?200 } else {201 unreachable!("resolver can't return this path")202 };203204 direct.push(path);205 if let Some(direct) = check_path(&direct)? {206 return Ok(direct);207 }208 for library_path in &self.library_paths {209 let mut cloned = library_path.clone();210 cloned.push(path);211 if let Some(cloned) = check_path(&cloned)? {212 return Ok(cloned);213 }214 }215 bail!(ImportFileNotFound(from.clone(), path.to_owned()))216 }217218 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {219 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {220 f.path()221 } else if id.downcast_ref::<SourceDirectory>().is_some() {222 bail!(ImportIsADirectory(id.clone()))223 } else if let Some(f) = id.downcast_ref::<SourceFifo>() {224 return Ok(f.1.to_vec());225 } else {226 unreachable!("other types are not supported in resolve");227 };228 let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;229 let mut out = Vec::new();230 file.read_to_end(&mut out)231 .map_err(|e| ImportIo(e.to_string()))?;232 Ok(out)233 }234235 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {236 self.resolve_from(&SourcePath::default(), path)237 }238}crates/jrsonnet-formatter/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-formatter/Cargo.toml
+++ b/crates/jrsonnet-formatter/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-formatter"
-description = "Jrsonnet code formatter"
+description = "Jsonnet source code formatter library"
+keywords = ["jsonnet", "formatter"]
+categories = ["development-tools"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-interner/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-interner/Cargo.toml
+++ b/crates/jrsonnet-interner/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-interner"
-description = "Jrsonnet string interning"
+description = "String interner used by jrsonnet"
+keywords = ["jsonnet", "interner", "string"]
+categories = ["data-structures", "memory-management"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-ir-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-ir-parser/Cargo.toml
+++ b/crates/jrsonnet-ir-parser/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-ir-parser"
-description = "Jrsonnet hand-rolled parser which parses source directly into IR"
+description = "Hand-rolled recursive-descent parser that produces jrsonnet IR directly"
+keywords = ["jsonnet", "parser"]
+categories = ["parser-implementations"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-ir/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-ir/Cargo.toml
+++ b/crates/jrsonnet-ir/Cargo.toml
@@ -1,6 +1,9 @@
[package]
name = "jrsonnet-ir"
-description = "jsonnet language parser and AST"
+description = "Intermediate representation for the jsonnet language used by jrsonnet"
+keywords = ["jsonnet", "ir", "ast"]
+categories = ["compilers"]
+readme = "README.adoc"
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-lexer/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-lexer/Cargo.toml
+++ b/crates/jrsonnet-lexer/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-lexer"
-description = "Jrsonnet lexer shared between rowan and hand-rolled parser"
+description = "Jsonnet lexer shared between the rowan-based and hand-rolled jrsonnet parsers"
+keywords = ["jsonnet", "lexer"]
+categories = ["parser-implementations"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-macros/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-macros/Cargo.toml
+++ b/crates/jrsonnet-macros/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-macros"
-description = "Macros to reduce boilerplate in jrsonnet-evaluator usage"
+description = "Procedural macros to reduce boilerplate in jrsonnet-evaluator usage"
+keywords = ["jsonnet", "macros"]
+categories = ["development-tools::procedural-macro-helpers"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-peg-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-peg-parser/Cargo.toml
+++ b/crates/jrsonnet-peg-parser/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-peg-parser"
-description = "Legacy peg-based parser for jrsonnet"
+description = "Legacy PEG-based parser producing jrsonnet IR"
+keywords = ["jsonnet", "parser", "peg"]
+categories = ["parser-implementations"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-rowan-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/Cargo.toml
+++ b/crates/jrsonnet-rowan-parser/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-rowan-parser"
-description = "Rowan-based CST for jrsonnet"
+description = "Rowan-based concrete syntax tree parser for jsonnet, suitable for tooling and LSP"
+keywords = ["jsonnet", "parser", "rowan", "cst"]
+categories = ["parser-implementations"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-rowan-parser/src/ast.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/ast.rs
+++ b/crates/jrsonnet-rowan-parser/src/ast.rs
@@ -77,9 +77,6 @@
use super::{AstChildren, AstNode, AstToken, SyntaxKind, SyntaxNode, SyntaxToken};
- pub fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
- parent.children().find_map(N::cast)
- }
pub fn token_child<N: AstToken>(parent: &SyntaxNode) -> Option<N> {
parent.children_with_tokens().find_map(|n| match n {
rowan::NodeOrToken::Node(_) => None,
crates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-stdlib"
-description = "jsonnet standard library packaged as crate"
+description = "Jsonnet standard library implementation for jrsonnet"
+keywords = ["jsonnet", "stdlib"]
+categories = ["template-engine"]
authors.workspace = true
edition.workspace = true
license.workspace = true
crates/jrsonnet-types/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-types/Cargo.toml
+++ b/crates/jrsonnet-types/Cargo.toml
@@ -1,6 +1,8 @@
[package]
name = "jrsonnet-types"
-description = "Jrsonnet type system definition"
+description = "Type system definitions used by jrsonnet"
+keywords = ["jsonnet", "types"]
+categories = ["data-structures"]
authors.workspace = true
edition.workspace = true
license.workspace = true