{"id":13671922,"url":"https://github.com/PoignardAzur/venial","last_synced_at":"2025-04-27T18:31:54.583Z","repository":{"id":39795388,"uuid":"464520139","full_name":"PoignardAzur/venial","owner":"PoignardAzur","description":"\"A very small syn\"","archived":false,"fork":false,"pushed_at":"2025-02-01T12:26:50.000Z","size":335,"stargazers_count":203,"open_issues_count":6,"forks_count":7,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-24T19:52:16.547Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PoignardAzur.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-28T14:42:46.000Z","updated_at":"2025-04-20T03:13:00.000Z","dependencies_parsed_at":"2024-01-24T00:36:19.556Z","dependency_job_id":"c91ca46c-816c-4e60-887a-cf3dd2df1dbf","html_url":"https://github.com/PoignardAzur/venial","commit_stats":{"total_commits":132,"total_committers":6,"mean_commits":22.0,"dds":"0.31818181818181823","last_synced_commit":"fc5d77dfba3d6ce8529d4cd0f46da50c51e29939"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PoignardAzur%2Fvenial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PoignardAzur%2Fvenial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PoignardAzur%2Fvenial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PoignardAzur%2Fvenial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PoignardAzur","download_url":"https://codeload.github.com/PoignardAzur/venial/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250699396,"owners_count":21473219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T09:01:22.122Z","updated_at":"2025-04-27T18:31:53.108Z","avatar_url":"https://github.com/PoignardAzur.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"[![crates.io](https://img.shields.io/crates/v/venial)](https://crates.io/crates/venial)\n[![docs.rs](https://docs.rs/venial/badge.svg)](https://docs.rs/venial/)\n[![license](https://img.shields.io/crates/l/venial)](https://github.com/PoignardAzur/venial/blob/main/LICENSE)\n\n## Lightweight parsing for Rust proc macros\n\nVenial is a small parser for Rust [proc macros](https://doc.rust-lang.org/reference/procedural-macros.html).\n\nWhen writing proc macros that need to parse Rust code (such as attribute and derive macros), the most common solution is to use the [syn](https://docs.rs/syn/latest/syn/index.html) crate. Syn can parse arbitrary valid Rust code, and even Rust-based DSLs, and return versatile data structures that can be inspected and mutated in powerful ways.\n\n**It's also extremely heavy**. In [one analysis](https://hackmd.io/mxdn4U58Su-UQXwzOHpHag?view#round-13-cargo-timing-opt-j8) of [lqd's early 2022 benchmark collection](https://github.com/lqd/rustc-benchmarking-data), the author estimates that syn is reponsible for 8% of compile times of the benchmark, which accounts for Rust's most popular crates. There are subtleties (eg this isn't necessarily critical path time, but syn is often in the critical path anyway), but the overall takeaway is clear: syn is expensive.\n\nAnd yet, a lot of the power of syn is often unneeded. If we look at the [crates that depend on syn](https://crates.io/crates/syn/reverse_dependencies), we can see that the 5 most downloaded are:\n\n- serde_derive\n- proc-macro-hack\n- pin-project-internal\n- anyhow\n- thiserror-impl\n\nOf these, proc-macro-hack is deprecated, and the other four only need to parse basic information on a type.\n\nOther popular reverse-dependencies of syn (such as futures-macro, tokios-macros, async-trait, etc) *do* use syn's more advanced features, but there's still room for a lightweight parser in proc-macros.\n\nVenial is that parser.\n\n\n## Design\n\nVenial is extremely simple. Most of its implementation is in the `parse.rs` file, which is about 350 lines at the time I'm writing this README. This is because the Rust language has a very clean syntax, especially for type declarations.\n\nVenial has no dependency besides proc-macro2 and quote.\n\nTo achieve this simplicity, venial makes several trade-offs:\n\n- It can only parse declarations (eg `struct MyStruct {}`). It can't parse expressions or statements. For now, only types and functions are supported.\n- It doesn't try to parse inside type expressions. For instance, if your struct includes a field like `foo_bar: \u0026mut Foo\u003cBar, dyn Foobariser\u003e`, venial will dutifully give you this type as a sequence of tokens and let you interpret it.\n- It doesn't attempt to recover gracefully from errors. Venial assumes you're running inside a derive or attribute macro, and thus that your input is statically guaranteed to be a valid type declaration. If it isn't, venial will summarily panic.\n\nNote though that venial will accept any syntactically valid declaration, even if it isn't semantically valid. The rule of thumb is \"if it compiles under a `#[cfg(FALSE)]`, venial will parse it without panicking\".\n\nThe only exception is enum discriminants. Venial only supports enum discriminants with a single token, or a token-group. Eg:\n\n```rust\nenum MyEnum {\n    A = 42,           // Ok\n    B = \"hello\",      // Ok\n    C = CONSTANT,     // Ok\n    D = FOO + BAR,    // MACRO ERROR\n    E = (FOO + BAR),  // Ok\n}\n```\n\nThis is because parsing complex discriminants requires arbitrary expression parsing, which is beyond the scope of this crate.\n\n(Note: venial currently panics on unsupported declarations, eg traits, aliases, etc. Also, function support is incomplete.)\n\n\n## Example\n\n```rust\nuse venial::{parse_declaration, Declaration};\nuse quote::quote;\n\nlet enum_type = parse_declaration(quote!(\n    enum Shape {\n        Square(Square),\n        Circle(Circle),\n        Triangle(Triangle),\n    }\n));\n\nlet enum_type = match enum_type {\n    Declaration::Enum(enum_type) =\u003e enum_type,\n    _ =\u003e unreachable!(),\n};\n\nassert_eq!(enum_type.variants[0].0.name, \"Square\");\nassert_eq!(enum_type.variants[1].0.name, \"Circle\");\nassert_eq!(enum_type.variants[2].0.name, \"Triangle\");\n```\n\n## Performance\n\nI haven't performed any kind of formal benchmark yet. That said, I compared [this fork of miniserde using venial](https://github.com/PoignardAzur/miniserde/tree/098bbbc3bac5812dc6613e334281d649fcbf88dc) to the [equivalent miniserde commit](https://github.com/dtolnay/miniserde/tree/4951a04384a69a3261e1a817ac4d146b119e953b), and got the following results:\n\n```sh\n$ cargo check -j1 # miniserde-venial, clean build\n    Finished dev [unoptimized + debuginfo] target(s) in 6.30s\n$ cargo check -j1 # miniserde, clean build\n    Finished dev [unoptimized + debuginfo] target(s) in 9.52s\n\n$ cargo check -j4 # miniserde-venial, clean build\n    Finished dev [unoptimized + debuginfo] target(s) in 3.17s\n$ cargo check -j4 # miniserde, clean build\n    Finished dev [unoptimized + debuginfo] target(s) in 4.79s\n```\n\nMy machine is desktop computer with an AMD Ryzen 7 1800x (8 cores, 16 threads), I have 32GB of RAM and a 2.5TB SSD.\n\nAs we can see, using venial instead of syn shaves about 3.2s off total build times in single-threaded builds, and 1.6s in 4-threaded builds.\n\nMost of the difference comes from syn and venial themselves: `cargo check --timings` shows that syn takes 2.11s to compile and venial takes 0.58s in 4-threaded builds.\n\nI'm not showing codegen builds, release mode builds, 16-threads builds and the like, but the trend stays roughly the same: for the miniserde project, switching to venial removes ~30% of the build time.\n\n### So... Is it worth it?\n\nThat's a fairly complicated question to answer. Two years after starting the project, my answer is now \"Only for niche use-cases\".\n\nIf you take the most optimistic interpretation, these results are great! On a single-threaded machine, switching shaves three seconds off, a whole third of the build time!\n\nIn reality, there are a lot of complicating factors:\n\n- Venial never improves incremental build times at all (since dependencies are cached, even when incremental compilation is off).\n- The gap between syn and venial is shorter with any amount of multithreading.\n- I have a fairly powerful computer. Laptops might get more of a benefit from venial.\n- In projects bigger than miniserde, syn is usually one of many libraries being compiled at the same time. In some cases that means the build time of syn doesn't matter that much since it's compiled in parallel with other libraries. In other cases syn is on the critical path.\n- In practice, most clean build are run by CI servers. To measure the usefulness of venial, you'd need to analyze the specs of the servers used in Github Actions / Gitlab CI / whatever [crater](https://github.com/rust-lang/crater) uses.\n\nAll in all, it's questionable whether the benefits are worth porting your derive crate from syn to venial (though my experience so far has been that porting isn't that hard).\n\nAnother thing to keep in mind is that this is a very young library. There has been very little effort to optimize it or profile it so far, and further versions may give a better build time reduction.\n\n**tl;dr:** You can probably shave off a few seconds off your clean builds with venial. Incremental builds see no benefits.\n\n## Contributions\n\nPull requests are welcome.\n\nI have no intention to work on venial in the near future myself, but I will still merge PRs.\n\nSome possible improvements:\n\n- Fixing the function declaration parser.\n- Finding and fixing any eventual bugs.\n- Porting other projects from syn to venial and comparing compile times.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPoignardAzur%2Fvenial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FPoignardAzur%2Fvenial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPoignardAzur%2Fvenial/lists"}