{"id":14155168,"url":"https://github.com/trevyn/turbosql","last_synced_at":"2025-09-12T15:04:54.156Z","repository":{"id":38842662,"uuid":"327262652","full_name":"trevyn/turbosql","owner":"trevyn","description":"An easy local data persistence layer for Rust, backed by SQLite.","archived":false,"fork":false,"pushed_at":"2025-08-14T05:45:37.000Z","size":306,"stargazers_count":238,"open_issues_count":15,"forks_count":11,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-09T03:56:54.516Z","etag":null,"topics":["database","rust","schema-migrations","sql","sqlite","sqlite3"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trevyn.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2021-01-06T09:29:25.000Z","updated_at":"2025-09-08T18:18:32.000Z","dependencies_parsed_at":"2023-01-24T00:17:05.846Z","dependency_job_id":"956ad666-4143-4176-b46d-dea1ab83da39","html_url":"https://github.com/trevyn/turbosql","commit_stats":{"total_commits":270,"total_committers":3,"mean_commits":90.0,"dds":0.5407407407407407,"last_synced_commit":"e1194818381b9b99dd93095012c127f56241f61b"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/trevyn/turbosql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevyn%2Fturbosql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevyn%2Fturbosql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevyn%2Fturbosql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevyn%2Fturbosql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trevyn","download_url":"https://codeload.github.com/trevyn/turbosql/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevyn%2Fturbosql/sbom","scorecard":{"id":898335,"data":{"date":"2025-08-11","repo":{"name":"github.com/trevyn/turbosql","commit":"9a6ca0c3cbcae1bd1321d7d6ac4958758f0287b9"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.8,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":4,"reason":"4 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":10,"reason":"GitHub workflow tokens follow principle of least privilege","details":["Info: topLevel 'contents' permission set to 'read': .github/workflows/ci.yml:9","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/trevyn/turbosql/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:78: update your workflow using https://app.stepsecurity.io/secureworkflow/trevyn/turbosql/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:83: update your workflow using https://app.stepsecurity.io/secureworkflow/trevyn/turbosql/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:84: update your workflow using https://app.stepsecurity.io/secureworkflow/trevyn/turbosql/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:96: update your workflow using https://app.stepsecurity.io/secureworkflow/trevyn/turbosql/ci.yml/main?enable=pin","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/ci.yml:90"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 5 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-24T14:34:52.773Z","repository_id":38842662,"created_at":"2025-08-24T14:34:52.773Z","updated_at":"2025-08-24T14:34:52.773Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274830038,"owners_count":25357865,"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","status":"online","status_checked_at":"2025-09-12T02:00:09.324Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["database","rust","schema-migrations","sql","sqlite","sqlite3"],"created_at":"2024-08-17T08:02:19.615Z","updated_at":"2025-09-12T15:04:54.112Z","avatar_url":"https://github.com/trevyn.png","language":"Rust","funding_links":[],"categories":["sqlite"],"sub_categories":[],"readme":"# Turbosql\n\n[\u003cimg alt=\"github\" src=\"https://img.shields.io/badge/github-trevyn/turbosql-663399?style=for-the-badge\u0026labelColor=555555\u0026logo=github\" height=\"27\"\u003e](https://github.com/trevyn/turbosql)\n[\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/v/turbosql.svg?style=for-the-badge\u0026color=ffc833\u0026logo=rust\" height=\"27\"\u003e](https://crates.io/crates/turbosql)\n[\u003cimg alt=\"docs.rs\" src=\"https://img.shields.io/badge/docs.rs-turbosql-353535?style=for-the-badge\u0026labelColor=555555\u0026logo=docs.rs\" height=\"27\"\u003e](https://docs.rs/turbosql)\n\nAn easy local data persistence layer, backed by SQLite.\n\n- Schema auto-defined by your Rust `struct`s\n- Automatic schema migrations\n- Super-simple basic `INSERT`/`SELECT`/`UPDATE`/`DELETE` operations\n- Use complex SQL if that's your jam\n- Validates all SQL (including user-supplied) at compile time\n\n## Usage\n\n```rust\nuse turbosql::{Turbosql, select, execute};\n\n#[derive(Turbosql, Default)]\nstruct Person {\n    rowid: Option\u003ci64\u003e, // rowid member required \u0026 enforced at compile time\n    name: Option\u003cString\u003e,\n    age: Option\u003ci64\u003e,\n    image_jpg: Option\u003cVec\u003cu8\u003e\u003e\n}\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n\n    let name = \"Joe\";\n\n    // INSERT a row\n    let rowid = Person {\n        name: Some(name.to_string()),\n        age: Some(42),\n        ..Default::default()\n    }.insert()?;\n\n    // SELECT all rows\n    let people = select!(Vec\u003cPerson\u003e)?;\n\n    // SELECT multiple rows with a predicate\n    let people = select!(Vec\u003cPerson\u003e \"WHERE age \u003e \" 21)?;\n\n    // SELECT a single row with a predicate\n    let mut person = select!(Person \"WHERE name = \" name)?;\n\n    // UPDATE based on rowid, rewrites all fields in database row\n    person.age = Some(43);\n    person.update()?;\n\n    // UPDATE with manual SQL\n    execute!(\"UPDATE person SET age = \" 44 \" WHERE name = \" name)?;\n\n    // DELETE\n    execute!(\"DELETE FROM person WHERE rowid = \" 1)?;\n\n    Ok(())\n}\n```\n\nSee [`integration_test.rs`](https://github.com/trevyn/turbosql/blob/main/turbosql/tests/integration_test.rs) or [trevyn/turbo](https://github.com/trevyn/turbo-also-historical/blob/main/turbo_server/src/schema.rs) for more usage examples!\n\n## Under the Hood\n\nTurbosql generates a SQLite schema and prepared queries for each struct:\n\n```rust\nuse turbosql::Turbosql;\n\n#[derive(Turbosql, Default)]\nstruct Person {\n    rowid: Option\u003ci64\u003e, // rowid member required \u0026 enforced\n    name: Option\u003cString\u003e,\n    age: Option\u003ci64\u003e,\n    image_jpg: Option\u003cVec\u003cu8\u003e\u003e\n}\n```\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;↓\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;auto-generates and validates the schema\n\n```sql\nCREATE TABLE person (\n    rowid INTEGER PRIMARY KEY,\n    name TEXT,\n    age INTEGER,\n    image_jpg BLOB,\n) STRICT\n\nINSERT INTO person (rowid, name, age, image_jpg) VALUES (?, ?, ?, ?)\n\nSELECT rowid, name, age, image_jpg FROM person\n```\n\nQueries with SQL predicates are also assembled and validated at compile time. Note that SQL types vs Rust types for parameter bindings are not currently checked at compile time.\n\n```rust,ignore\nlet people = select!(Vec\u003cPerson\u003e \"WHERE age \u003e ?\", 21);\n```\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;↓\n\n```sql\nSELECT rowid, name, age, image_jpg FROM person WHERE age \u003e ?\n```\n\n## Automatic Schema Migrations\n\nAt compile time, the `#[derive(Turbosql)]` macro runs and creates a `migrations.toml` file in your project root that describes the database schema.\n\nEach time you change a `struct` declaration and the macro is re-run (e.g. by `cargo` or `rust-analyzer`), migration SQL statements are generated that update the database schema. These new statements are recorded in `migrations.toml`, and are automatically embedded in your binary.\n\n```rust\n#[derive(turbosql::Turbosql, Default)]\nstruct Person {\n    rowid: Option\u003ci64\u003e,\n    name: Option\u003cString\u003e\n}\n```\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;↓\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;auto-generates `migrations.toml`\n\n```toml\nmigrations_append_only = [\n  'CREATE TABLE person(rowid INTEGER PRIMARY KEY) STRICT',\n  'ALTER TABLE person ADD COLUMN name TEXT',\n]\noutput_generated_schema_for_your_information_do_not_edit = '''\n  CREATE TABLE person (\n    rowid INTEGER PRIMARY KEY,\n    name TEXT\n  ) STRICT\n'''\n```\n\nWhen your schema changes, any new version of your binary will automatically migrate any older database file to the current schema by applying the appropriate migrations in sequence.\n\nThis migration process is a one-way ratchet: Old versions of the binary run on a database file with a newer schema will detect a schema mismatch and will be blocked from operating on the futuristically-schema'd database file.\n\nUnused or reverted migrations that are created during development can be manually removed from `migrations.toml` before being released, but any database files that have already applied these deleted migrations will error and must be rebuilt. Proceed with care. When in doubt, refrain from manually editing `migrations.toml`, and everything should work fine.\n\n- Just declare and freely append fields to your `struct`s.\n- Check out the `migrations.toml` file that is generated in your project root to see what's happening.\n- If you run into any weird compiler errors, try just re-compiling first; depending on the order the proc macros run, sometimes it just needs a little push to get in sync after a schema change.\n- Schema migrations are one-way, append-only. This is similar to the approach taken by [leafac/sqlite-migration](https://github.com/leafac/sqlite-migration#no-down-migrations) for the Node.js ecosystem; see that project for a discussion of the advantages!\n- On launch, versions of your binary built with a newer schema will automatically apply the appropriate migrations to an older database.\n- If you're feeling adventurous, you can add your own schema migration entries to the bottom of the list. (For creating indexes, etc.)\n- You can hand-write complex migrations as well, see [turbo/migrations.toml](https://github.com/trevyn/turbo-also-historical/blob/main/migrations.toml) for some examples.\n- Please open a GitHub issue with any questions or suggestions!\n\n## Where's my data?\n\nThe SQLite database file is created in the directory returned by [`directories_next::ProjectDirs::data_dir()`](https://docs.rs/directories-next/%5E2.0.0/directories_next/struct.ProjectDirs.html#method.data_dir) + your executable's filename stem, which resolves to something like:\n\n\u003ctable\u003e\u003ctr\u003e\u003ctd\u003eLinux\u003c/td\u003e\u003ctd\u003e\u003cbr\u003e\n\n`$XDG_DATA_HOME`/`{exe_name}` or `$HOME`/.local/share/`{exe_name}` _/home/alice/.local/share/fooapp/fooapp.sqlite_\n\n\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003emacOS\u003c/td\u003e\u003ctd\u003e\u003cbr\u003e\n\n`$HOME`/Library/Application\u0026nbsp;Support/`{exe_name}` _/Users/Alice/Library/Application\u0026nbsp;Support/org.fooapp.fooapp/fooapp.sqlite_\n\n\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003eWindows\u003c/td\u003e\u003ctd\u003e\u003cbr\u003e\n\n`{FOLDERID_LocalAppData}`\\\\`{exe_name}`\\\\data _C:\\Users\\Alice\\AppData\\Local\\fooapp\\fooapp\\data\\fooapp.sqlite_\n\n\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\n## Transactions and `async`\n\nSQLite, and indeed many filesystems in general, only provide blocking (synchronous) APIs. The correct approach when using blocking APIs in a Rust `async` ecosystem is to use your executor's facility for running a closure on a thread pool in which blocking is expected. For example:\n\n```rust\n#[derive(turbosql::Turbosql, Default)]\nstruct Person {\n    rowid: Option\u003ci64\u003e,\n    name: Option\u003cString\u003e\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let person = tokio::task::spawn_blocking(|| {\n        turbosql::select!(Option\u003cPerson\u003e \"WHERE name = ?\", \"Joe\")\n    }).await??;\n    Ok(())\n}\n```\n\n(Note that `spawn_blocking` returns a `JoinHandle` that must itself be unwrapped, hence the need for `??` near the end of these examples.)\n\nUnder the hood, Turbosql uses persistent [`thread_local`](https://doc.rust-lang.org/std/macro.thread_local.html) database connections, so a continuous sequence of database calls from the same thread are guaranteed to use the same exclusive database connection. Thus, `async` transactions can be performed as such:\n\n```rust\nuse turbosql::{Turbosql, select, execute};\n\n#[derive(Turbosql, Default)]\nstruct Person {\n    rowid: Option\u003ci64\u003e,\n    age: Option\u003ci64\u003e\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    tokio::task::spawn_blocking(|| -\u003e Result\u003c(), turbosql::Error\u003e {\n        Person { rowid: None, age: Some(21) }.insert()?;\n        execute!(\"BEGIN IMMEDIATE TRANSACTION\")?;\n        let p = select!(Person \"WHERE rowid = ?\", 1)?;\n        // [ ...do any other blocking things... ]\n        execute!(\n            \"UPDATE person SET age = ? WHERE rowid = ?\",\n            p.age.unwrap_or_default() + 1,\n            1\n        )?;\n        execute!(\"COMMIT\")?;\n        Ok(())\n    }).await??;\n    Ok(())\n}\n```\n\nTurbosql sets a SQLite [`busy_timeout`](https://sqlite.org/c3ref/busy_timeout.html) of 3 seconds, so any table lock contention is automatically re-tried up to that duration, after which the command that was unable to acquire a lock will return with an error.\n\nFor further discussion of Turbosql's approach to `async` and transactions, see [https://github.com/trevyn/turbosql/issues/4](https://github.com/trevyn/turbosql/issues/4). Ideas for improvements to the ergonomics of the solution are very welcome.\n\n## `-wal` and `-shm` files\n\nSQLite is an extremely reliable database engine, but it helps to understand how it interfaces with the filesystem. The main `.sqlite` file contains the bulk of the database. During database writes, SQLite also creates `.sqlite-wal` and `.sqlite-shm` files. If the host process is terminated without flushing writes, you may end up with these three files when you expected to have a single file. This is always fine; on next launch, SQLite knows how to resolve any interrupted writes and make sense of the world. However, if the `-wal` and/or `-shm` files are present, they **must be considered essential to database integrity**. Deleting them may result in a corrupted database. See [https://sqlite.org/tempfiles.html](https://sqlite.org/tempfiles.html).\n\n## Example Query Forms\n\nCheck [`integration_test.rs`](https://github.com/trevyn/turbosql/blob/main/turbosql/tests/integration_test.rs) for more examples of what works and is tested in CI.\n\n\u003ctable\u003e\n\n\u003ctr\u003e\u003ctd\u003e\u003cb\u003e\u0026nbsp;Primitive\u0026nbsp;type\u003c/b\u003e\u003c/td\u003e\u003ctd\u003e\u003cbr\u003e\n\n```rust,ignore\nlet result = select!(String \"SELECT name FROM person\")?;\n```\n\nReturns one value cast to specified type, returns `Error` if no rows available.\n\n```rust,ignore\nlet result = select!(String \"name FROM person WHERE rowid = ?\", rowid)?;\n```\n\n`SELECT` keyword is **always optional** when using `select!`; it's added automatically as needed.\u003cbr\u003eParameter binding is straightforward.\n\n\u003c/td\u003e\u003c/tr\u003e\n\n\u003ctr\u003e\u003ctd\u003e\u0026nbsp;\u003cb\u003e\u003ccode\u003eVec\u0026lt;_\u0026gt;\u003c/code\u003e\u003c/b\u003e\u003c/td\u003e\u003ctd\u003e\u003cbr\u003e\n\n```rust,ignore\nlet result = select!(Vec\u003cString\u003e \"name FROM person\")?;\n```\n\nReturns `Vec` containing another type. If no rows, returns empty `Vec`.\n\n\u003c/td\u003e\u003c/tr\u003e\n\n\u003ctr\u003e\u003ctd\u003e\u0026nbsp;\u003cb\u003e\u003ccode\u003eOption\u0026lt;_\u0026gt;\u003c/code\u003e\u003c/b\u003e\u003c/td\u003e\u003ctd\u003e\u003cbr\u003e\n\n```rust,ignore\nlet result = select!(Option\u003cString\u003e \"name FROM person\")?;\n```\n\nReturns `Ok(None)` if no rows, `Error(_)` on error.\n\n\u003c/td\u003e\u003c/tr\u003e\n\n\u003ctr\u003e\u003ctd\u003e\u003cb\u003e\u0026nbsp;Your struct\u003c/b\u003e\u003c/td\u003e\u003ctd\u003e\u003cbr\u003e\n\n```rust,ignore\nlet result = select!(Person \"WHERE name = ?\", name)?;\n```\n\nColumn list and table name are optional if type is a `#[derive(Turbosql)]` struct.\n\n```rust,ignore\nlet result = select!(Vec\u003cNameAndAdult\u003e \"name, age \u003e= 18 AS adult FROM person\")?;\n```\n\nYou can use other struct types as well; column names must match the struct and you must specify the source table in the SQL.\u003cbr\u003eImplement `Default` to avoid specifying unused column names.\u003cbr\u003e(And, of course, you can put it all in a `Vec` or `Option` as well.)\n\n```rust,ignore\nlet result = select!(Vec\u003cPerson\u003e)?;\n```\n\nSometimes everything is optional; this example will retrieve all `Person` rows.\n\n\u003c/td\u003e\u003c/tr\u003e\n\n\u003c/table\u003e\n\u003cbr\u003e\n\n## \"turbosql\" or \"Turbosql\"?\n\nYour choice, but you _definitely_ do not want to capitalize any of the _other_ letters in the name! ;)\n\n### License: MIT OR Apache-2.0 OR CC0-1.0 (public domain)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrevyn%2Fturbosql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrevyn%2Fturbosql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrevyn%2Fturbosql/lists"}