├── .gitmodules ├── .gitignore ├── examples └── hello.rs ├── .github └── workflows │ └── ci.yaml ├── LICENSE ├── Cargo.toml ├── cmrc ├── pinyin.txt │ └── lib.cpp └── include │ └── cmrc │ └── cmrc.hpp ├── src ├── ffi.rs └── lib.rs ├── CHANGELOG.md ├── README.md └── Cargo.lock /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "simple"] 2 | path = simple 3 | url = https://github.com/wangfenjin/simple.git 4 | 5 | [submodule "cppjieba"] 6 | path = cppjieba 7 | url = https://github.com/yanyiwu/cppjieba.git 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ### Cargo ### 4 | !/.gitignore 5 | !/Cargo.lock 6 | !/Cargo.toml 7 | 8 | ### Project ### 9 | !/src 10 | !/build.rs 11 | !/examples 12 | !/cmrc 13 | 14 | ## modules ## 15 | !/.gitmodules 16 | !/simple 17 | !/cppjieba 18 | 19 | ## docs ## 20 | !/README.md 21 | !/CHANGELOG.md 22 | !/LICENSE 23 | 24 | ## ci ## 25 | !/.github 26 | -------------------------------------------------------------------------------- /examples/hello.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use tempfile::tempdir; 3 | use rusqlite::Connection; 4 | 5 | fn main() -> Result<()> { 6 | libsimple::enable_auto_extension()?; 7 | let dir = tempdir()?; 8 | libsimple::release_dict(&dir)?; 9 | 10 | let conn = Connection::open_in_memory()?; 11 | libsimple::set_dict(&conn, &dir)?; 12 | 13 | conn.execute_batch(" 14 | CREATE VIRTUAL TABLE d USING fts5(id, text, tokenize = 'simple'); 15 | INSERT INTO d (id, text) VALUES (1, '中华人民共和国国歌'); 16 | INSERT INTO d (id, text) VALUES (2, '周杰伦'); 17 | ")?; 18 | assert_eq!(1, conn.query_row( 19 | "SELECT id FROM d WHERE text MATCH jieba_query('中华国歌')", 20 | [], |row| row.get::<_, i64>(0), 21 | )?); 22 | assert_eq!(2, conn.query_row( 23 | "SELECT id FROM d WHERE text MATCH simple_query('zhoujiel')", 24 | [], |row| row.get::<_, i64>(0), 25 | )?); 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | push: 8 | workflow_dispatch: 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | RUST_BACKTRACE: full 13 | 14 | jobs: 15 | test: 16 | strategy: 17 | matrix: 18 | os: 19 | - name: linux-amd64 20 | runner: ubuntu-latest 21 | - name: linux-arm64 22 | runner: ubuntu-24.04-arm 23 | - name: windows-amd64 24 | runner: windows-latest 25 | - name: windows-arm64 26 | runner: windows-11-arm 27 | - name: macos-arm64 28 | runner: macos-latest 29 | - name: macos-amd64 30 | runner: macos-15-intel 31 | fail-fast: false 32 | name: Test on ${{ matrix.os.name }} 33 | runs-on: ${{ matrix.os.runner }} 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v6 37 | with: 38 | submodules: recursive 39 | - name: Check rust version 40 | run: rustup --version 41 | - name: Run test 42 | run: cargo test 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 xuxiaocheng0201 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libsimple" 3 | version = "0.6.1" 4 | authors = ["xuxiaocheng <2762267080@qq.com>"] 5 | edition = "2024" 6 | rust-version = "1.85.0" 7 | description = "Rust bindings to simple, a SQLite3 fts5 tokenizer which supports Chinese and PinYin." 8 | readme = "README.md" 9 | license = "MIT" 10 | repository = "https://github.com/xuxiaocheng0201/libsimple/" 11 | documentation = "https://docs.rs/libsimple/" 12 | categories = ["database"] 13 | keywords = ["sqlite", "extension", "sqlite-extension", "fts5", "tokenizer"] 14 | 15 | include = [ 16 | "/Cargo.toml", 17 | "/README.md", 18 | "/src", 19 | "/build.rs", 20 | "/examples", 21 | 22 | "/cmrc", 23 | "/cppjieba/include", 24 | "/cppjieba/dict", 25 | "!/cppjieba/dict/pos_dict", 26 | "/cppjieba/deps/limonp/include", 27 | "/simple/src", 28 | "/simple/contrib/sqlite3", 29 | 30 | "**/LICENSE", 31 | "!**/*.cmake", 32 | "!**/CMakeLists.txt" 33 | ] 34 | 35 | [features] 36 | default = ["jieba"] 37 | jieba = [] 38 | 39 | [build-dependencies] 40 | cc = { version = "^1", features = ["parallel"] } 41 | 42 | [dependencies] 43 | rusqlite = ">=0.32,<1.0" 44 | better_embedded = { version = "~0.4", default-features = false } 45 | 46 | [dev-dependencies] 47 | anyhow = "^1" 48 | rusqlite = { version = ">=0.32,<1.0", features = ["bundled"] } 49 | tempfile = "^3" 50 | 51 | [package.metadata.docs.rs] 52 | all-features = true 53 | rustdoc-args = ["--cfg", "docsrs"] 54 | 55 | [[example]] 56 | name = "hello" 57 | test = true 58 | -------------------------------------------------------------------------------- /cmrc/pinyin.txt/lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace cmrc { 6 | namespace pinyin_text { 7 | 8 | namespace res_chars { 9 | // These are the files which are available in this resource library 10 | // Pointers to contrib/pinyin.txt 11 | extern const char* const f_260e_contrib_pinyin_txt_begin; 12 | extern const char* const f_260e_contrib_pinyin_txt_end; 13 | } 14 | 15 | namespace { 16 | 17 | const cmrc::detail::index_type& 18 | get_root_index() { 19 | static cmrc::detail::directory root_directory_; 20 | static cmrc::detail::file_or_directory root_directory_fod{root_directory_}; 21 | static cmrc::detail::index_type root_index; 22 | root_index.emplace("", &root_directory_fod); 23 | struct dir_inl { 24 | class cmrc::detail::directory& directory; 25 | }; 26 | dir_inl root_directory_dir{root_directory_}; 27 | (void)root_directory_dir; 28 | static auto f_1aca_contrib_dir = root_directory_dir.directory.add_subdir("contrib"); 29 | root_index.emplace("contrib", &f_1aca_contrib_dir.index_entry); 30 | root_index.emplace( 31 | "contrib/pinyin.txt", 32 | f_1aca_contrib_dir.directory.add_file( 33 | "pinyin.txt", 34 | res_chars::f_260e_contrib_pinyin_txt_begin, 35 | res_chars::f_260e_contrib_pinyin_txt_end 36 | ) 37 | ); 38 | return root_index; 39 | } 40 | 41 | } 42 | 43 | cmrc::embedded_filesystem get_filesystem() { 44 | static auto& index = get_root_index(); 45 | return cmrc::embedded_filesystem{index}; 46 | } 47 | 48 | } // pinyin_text 49 | } // cmrc 50 | -------------------------------------------------------------------------------- /src/ffi.rs: -------------------------------------------------------------------------------- 1 | //! Raw FFI bindings to simple. 2 | 3 | use std::ffi::{c_char, c_int, CStr}; 4 | 5 | use rusqlite::{Error, ffi, Result}; 6 | 7 | unsafe extern "C" { 8 | /// The entrypoint for the [simple](https://github.com/wangfenjin/simple) extension. 9 | pub fn sqlite3_simple_init(db: *mut ffi::sqlite3, pz_err_msg: *mut *mut c_char, p_api: *const ffi::sqlite3_api_routines) -> c_int; 10 | 11 | 12 | /// The sqlite function entrypoint for `simple_query` function. 13 | pub fn simple_query(ctx: *mut ffi::sqlite3_context, argc: c_int, argv: *mut *mut ffi::sqlite3_value); 14 | 15 | #[cfg(feature = "jieba")] 16 | /// The sqlite function entrypoint for `jieba_dict` function. 17 | pub fn jieba_dict(ctx: *mut ffi::sqlite3_context, argc: c_int, argv: *mut *mut ffi::sqlite3_value); 18 | 19 | #[cfg(feature = "jieba")] 20 | /// The sqlite function entrypoint for `jieba_query` function. 21 | pub fn jieba_query(ctx: *mut ffi::sqlite3_context, argc: c_int, argv: *mut *mut ffi::sqlite3_value); 22 | 23 | 24 | /// The sqlite fts5 function entrypoint for `simple_highlight` function. 25 | pub fn simple_highlight(api: *const ffi::Fts5ExtensionApi, fts: *mut ffi::Fts5Context, ctx: *mut ffi::sqlite3_context, argc: c_int, argv: *mut *mut ffi::sqlite3_value); 26 | 27 | /// The sqlite fts5 function entrypoint for `simple_highlight_pos` function. 28 | pub fn simple_highlight_pos(api: *const ffi::Fts5ExtensionApi, fts: *mut ffi::Fts5Context, ctx: *mut ffi::sqlite3_context, argc: c_int, argv: *mut *mut ffi::sqlite3_value); 29 | 30 | /// The sqlite fts5 function entrypoint for `simple_snippet` function. 31 | pub fn simple_snippet(api: *const ffi::Fts5ExtensionApi, fts: *mut ffi::Fts5Context, ctx: *mut ffi::sqlite3_context, argc: c_int, argv: *mut *mut ffi::sqlite3_value); 32 | } 33 | 34 | /// This is a re-exported and enhanced version of [`rusqlite::error::check(res)`](rusqlite::error::check) 35 | #[doc(hidden)] 36 | pub fn check_err(res: c_int) -> Result<()> { 37 | if res == ffi::SQLITE_OK { 38 | return Ok(()); 39 | } 40 | Err(get_err(res)) 41 | } 42 | 43 | #[cold] 44 | fn get_err(res: c_int) -> Error { 45 | let err = unsafe { ffi::sqlite3_errstr(res) }; 46 | if err.is_null() { 47 | return Error::SqliteFailure(ffi::Error::new(res), None); 48 | } 49 | let msg = unsafe { CStr::from_ptr(err) }.to_str(); 50 | match msg { 51 | Ok(msg) => Error::SqliteFailure(ffi::Error::new(res), Some(msg.to_string())), 52 | Err(err) => Error::Utf8Error(err), 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.6.1] - 2025-8-10 11 | 12 | ### Changed 13 | 14 | * 更新 `simple` 补丁 [b88f87035795cfdda71ba8629967973ed00e404c](https://github.com/wangfenjin/simple/tree/b88f87035795cfdda71ba8629967973ed00e404c) 15 | * 关闭 `better_embedded` 默认功能 16 | 17 | ## [0.6.0] - 2025-5-27 18 | 19 | ### Changed 20 | 21 | * 更新 `rusqlite` 到 `>=0.32,<1.0` 22 | * 更新 `cppjieba` 到 [v5.6.0](https://github.com/yanyiwu/cppjieba/commit/294755fab1ea0c2544442b27cb7d2ad9bcc4ba01) 23 | 24 | ## [0.5.0] - 2025-4-24 25 | 26 | ### Changed 27 | 28 | * 更新 `rusqlite` 到 ~0.35 29 | * 更新 `simple` 到 [v0.5.2](https://github.com/wangfenjin/simple/releases/tag/v0.5.2) 30 | * 更新 `cppjieba` 到 [v5.5.0](https://github.com/yanyiwu/cppjieba/commit/3732abc0e5548c96b4a6ea55113a02df97acf761) 31 | 32 | ## [0.4.0] - 2025-3-6 33 | 34 | ### Changed 35 | 36 | * 使用 edition 2024 37 | * MSRV 为 1.85.0 38 | 39 | ## [0.3.7] - 2025-3-6 40 | 41 | ### Changed 42 | 43 | * 更新 `rusqlite` 到 ~0.34 44 | 45 | ## [0.3.6] - 2025-1-25 46 | 47 | ### Changed 48 | 49 | * 更新 `cppjieba` 到 [v5.4.0](https://github.com/yanyiwu/cppjieba/commit/9b45e084a3153b9a60ead6c8fecc97345c17da15) 50 | 51 | ## [0.3.5] - 2025-1-20 52 | 53 | ### Changed 54 | 55 | * 更新 `rusqlite` 到 ~0.33 56 | 57 | ## [0.3.4] - 2024-10-7 58 | 59 | ### Added 60 | 61 | * 添加 highlight 相关 ffi 入口点 62 | 63 | ## [0.3.3] - 2024-9-13 64 | 65 | ### Fixed 66 | 67 | * 修复 MSVC 编译 D8021 错误 68 | 69 | ## [0.3.2] - 2024-9-5 70 | 71 | ### Fixed 72 | 73 | * 优化 `release_dict` 行为,不始终覆盖文件 74 | 75 | ## [0.3.1] - 2024-7-25 76 | 77 | ### Changed 78 | 79 | * 更新 `rusqlite` 到 ~0.32 80 | * 修改 `sqlite3_simple_init` 方法签名 81 | 82 | ## [0.3.0] - 2024-5-20 83 | 84 | ### Added 85 | 86 | * 添加 `disable_auto_extension` 方法 87 | * 添加更多 ffi 接口 88 | 89 | ## [0.2.2] - 2024-5-20 90 | 91 | ### Added 92 | 93 | * 添加 hello example 94 | 95 | ## [0.2.1] - 2024-4-16 96 | 97 | ### Changed 98 | 99 | * 优化文档 100 | * 测试 `sqlcipher` 101 | 102 | ## [0.2.0] - 2024-4-16 103 | 104 | ### Added 105 | 106 | * Bundled 构建,支持离线 107 | * 添加 `jieba` feature,可选编译 108 | 109 | ### Changed 110 | 111 | * 重构 API 112 | 113 | ## [0.1.0] - 2024-4-15 114 | 115 | ### Added 116 | 117 | * 支持 `simple` [v0.4.0](https://github.com/wangfenjin/simple/releases/tag/v0.4.0) 118 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 3 | #![warn(missing_docs)] 4 | 5 | #[cfg(feature = "jieba")] 6 | use std::path::Path; 7 | 8 | use better_embedded::strategies::DefaultCheckStrategy; 9 | use rusqlite::ffi::{sqlite3_auto_extension, sqlite3_cancel_auto_extension}; 10 | 11 | use crate::ffi::sqlite3_simple_init; 12 | 13 | pub mod ffi; 14 | 15 | /// Enable sqlite3_simple_init() as an auto extension. 16 | pub fn enable_auto_extension() -> rusqlite::Result<()> { 17 | let res = unsafe { sqlite3_auto_extension(Some(sqlite3_simple_init)) }; 18 | ffi::check_err(res) 19 | } 20 | 21 | /// Disable sqlite3_simple_init() as an auto extension. 22 | pub fn disable_auto_extension() -> rusqlite::Result<()> { 23 | let res = unsafe { sqlite3_cancel_auto_extension(Some(sqlite3_simple_init)) }; 24 | ffi::check_err(res) 25 | } 26 | 27 | /// Release dict files into directory. 28 | /// Only need to call this method once. 29 | /// 30 | /// Then you may call [`set_dict`] for each connection. 31 | #[cfg(feature = "jieba")] 32 | pub fn release_dict(directory: impl AsRef) -> std::io::Result<()> { 33 | let directory = directory.as_ref().to_path_buf(); 34 | if !directory.is_dir() { std::fs::create_dir_all(&directory)?; } 35 | 36 | macro_rules! embedded_file { 37 | ($target: ident, $source: expr) => { 38 | let file = include_bytes!(concat!("../cppjieba/dict/", $source)); 39 | let target = $target.join($source); 40 | better_embedded::release_file_with_check(file, &target, DefaultCheckStrategy::config())?; 41 | }; 42 | } 43 | embedded_file!(directory, "jieba.dict.utf8"); 44 | embedded_file!(directory, "user.dict.utf8"); 45 | embedded_file!(directory, "hmm_model.utf8"); 46 | embedded_file!(directory, "idf.utf8"); 47 | embedded_file!(directory, "stop_words.utf8"); 48 | 49 | Ok(()) 50 | } 51 | 52 | /// Only need to call once for each connection, 53 | /// but must call this function before using sql `jieba_query`. 54 | /// 55 | /// You should call [`release_dict`] first. 56 | #[cfg(feature = "jieba")] 57 | pub fn set_dict(connection: &rusqlite::Connection, directory: impl AsRef) -> rusqlite::Result<()> { 58 | let directory = directory.as_ref(); 59 | let directory = directory.to_str() 60 | .ok_or_else(|| rusqlite::Error::InvalidPath(directory.to_path_buf()))?; 61 | connection.query_row("SELECT jieba_dict(?)", rusqlite::params![directory], |_| Ok(())) 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | #[test] 67 | fn test() -> anyhow::Result<()> { 68 | crate::enable_auto_extension()?; 69 | let dir = tempfile::tempdir()?; 70 | crate::release_dict(&dir)?; 71 | 72 | let conn = rusqlite::Connection::open_in_memory()?; 73 | crate::set_dict(&conn, &dir)?; 74 | conn.execute_batch(" 75 | PRAGMA key = '123456'; 76 | CREATE TABLE singer (id INTEGER, name TEXT); 77 | CREATE VIRTUAL TABLE d USING fts5(id, name, tokenize = 'simple'); 78 | CREATE TRIGGER trigger AFTER INSERT ON singer BEGIN 79 | INSERT INTO d(id, name) VALUES (new.id, new.name); 80 | END; 81 | INSERT INTO singer (id, name) VALUES (1, '中华人民共和国国歌'); 82 | ")?; 83 | assert_eq!(1, conn.query_row( 84 | "SELECT id FROM d WHERE name MATCH jieba_query('中华国歌')", 85 | [], |row| row.get::<_, i64>(0) 86 | )?); 87 | Ok(()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libsimple 2 | 3 | [![Crate](https://img.shields.io/crates/v/libsimple.svg)](https://crates.io/crates/libsimple) 4 | [![GitHub last commit](https://img.shields.io/github/last-commit/xuxiaocheng0201/libsimple)](https://github.com/xuxiaocheng0201/libsimple/commits/master) 5 | [![GitHub issues](https://img.shields.io/github/issues-raw/xuxiaocheng0201/libsimple)](https://github.com/xuxiaocheng0201/libsimple/issues) 6 | [![GitHub pull requests](https://img.shields.io/github/issues-pr/xuxiaocheng0201/libsimple)](https://github.com/xuxiaocheng0201/libsimple/pulls) 7 | [![GitHub](https://img.shields.io/github/license/xuxiaocheng0201/libsimple)](https://github.com/xuxiaocheng0201/libsimple/blob/master/LICENSE) 8 | 9 | # Description 10 | 11 | Rust bindings to [simple](https://github.com/wangfenjin/simple), 12 | a SQLite3 fts5 tokenizer which supports Chinese and PinYin. 13 | 14 | 15 | # Usage 16 | 17 | Add this to your `Cargo.toml`: 18 | 19 | ```toml 20 | [dependencies] 21 | libsimple = "~0.6" 22 | ``` 23 | 24 | 25 | # Example 26 | 27 | ```rust 28 | use anyhow::Result; 29 | use tempfile::tempdir; 30 | 31 | fn main() -> Result<()> { 32 | libsimple::enable_auto_extension()?; 33 | let dir = tempdir()?; 34 | libsimple::release_dict(&dir)?; 35 | 36 | let conn = rusqlite::Connection::open_in_memory()?; 37 | libsimple::set_dict(&conn, &dir)?; 38 | 39 | conn.execute_batch(" 40 | CREATE VIRTUAL TABLE d USING fts5(id, text, tokenize = 'simple'); 41 | INSERT INTO d (id, text) VALUES (1, '中华人民共和国国歌'); 42 | INSERT INTO d (id, text) VALUES (2, '周杰伦'); 43 | ")?; 44 | assert_eq!(1, conn.query_row( 45 | "SELECT id FROM d WHERE text MATCH jieba_query('中华国歌')", 46 | [], |row| row.get::<_, i64>(0) 47 | )?); 48 | assert_eq!(2, conn.query_row( 49 | "SELECT id FROM d WHERE text MATCH simple_query('zhoujiel')", 50 | [], |row| row.get::<_, i64>(0) 51 | )?); 52 | Ok(()) 53 | } 54 | ``` 55 | 56 | 57 | # License 58 | 59 | Licensed under MIT license ([LICENSE](LICENSE) or ) 60 | 61 | 62 | # Version map 63 | 64 | This is the compatible version map between `libsimple` and `rusqlite`: 65 | 66 | | `libsimple` version | `rusqlite` version | 67 | |---------------------|--------------------| 68 | | =0.6.1 | >=0.32,<1.0 | 69 | | =0.6.0 | >=0.32,<1.0 | 70 | | =0.5.0 | ~0.35 | 71 | | =0.4.0 | ~0.34 | 72 | | =0.3.7 | ~0.34 | 73 | | =0.3.6 | ~0.33 | 74 | | =0.3.5 | ~0.33 | 75 | | =0.3.4 | ~0.32 | 76 | | =0.3.3 | ~0.32 | 77 | | =0.3.2 | ~0.32 | 78 | | =0.3.1 | ~0.32 | 79 | | =0.3.0 | ~0.31 | 80 | | =0.2.2 | ~0.31 | 81 | | =0.2.1 | ~0.31 | 82 | | =0.2.0 | ~0.31 | 83 | | =0.1.0 | ~0.31 | 84 | 85 | 86 | # Generate CMRC 87 | 88 | This is only required when the `simple/contrib/pinyin.txt` updated. 89 | Normal user can ignore this. 90 | 91 | ```bash 92 | cd simple && mkdir build && cd build 93 | cmake .. -DBUILD_SQLITE3=off -DSIMPLE_WITH_JIEBA=off -DBUILD_TEST_EXAMPLE=off 94 | make 95 | cp -f _cmrc/include/cmrc/cmrc.hpp ../../cmrc/include/cmrc/cmrc.hpp 96 | cp -f __cmrc_PINYIN_TEXT/lib.cpp ../../cmrc/pinyin.txt/lib.cpp 97 | cp -f __cmrc_PINYIN_TEXT/intermediate/contrib/pinyin.txt.cpp ../../cmrc/pinyin.txt/pinyin.txt.cpp 98 | cd .. && rm -r build && cd .. 99 | ``` 100 | -------------------------------------------------------------------------------- /cmrc/include/cmrc/cmrc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CMRC_CMRC_HPP_INCLUDED 2 | #define CMRC_CMRC_HPP_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if !(defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) || defined(CMRC_NO_EXCEPTIONS)) 15 | #define CMRC_NO_EXCEPTIONS 1 16 | #endif 17 | 18 | namespace cmrc { namespace detail { struct dummy; } } 19 | 20 | #define CMRC_DECLARE(libid) \ 21 | namespace cmrc { namespace detail { \ 22 | struct dummy; \ 23 | static_assert(std::is_same::value, "CMRC_DECLARE() must only appear at the global namespace"); \ 24 | } } \ 25 | namespace cmrc { namespace libid { \ 26 | cmrc::embedded_filesystem get_filesystem(); \ 27 | } } static_assert(true, "") 28 | 29 | namespace cmrc { 30 | 31 | class file { 32 | const char* _begin = nullptr; 33 | const char* _end = nullptr; 34 | 35 | public: 36 | using iterator = const char*; 37 | using const_iterator = iterator; 38 | iterator begin() const noexcept { return _begin; } 39 | iterator cbegin() const noexcept { return _begin; } 40 | iterator end() const noexcept { return _end; } 41 | iterator cend() const noexcept { return _end; } 42 | std::size_t size() const { return static_cast(std::distance(begin(), end())); } 43 | 44 | file() = default; 45 | file(iterator beg, iterator end) noexcept : _begin(beg), _end(end) {} 46 | }; 47 | 48 | class directory_entry; 49 | 50 | namespace detail { 51 | 52 | class directory; 53 | class file_data; 54 | 55 | class file_or_directory { 56 | union _data_t { 57 | class file_data* file_data; 58 | class directory* directory; 59 | } _data; 60 | bool _is_file = true; 61 | 62 | public: 63 | explicit file_or_directory(file_data& f) { 64 | _data.file_data = &f; 65 | } 66 | explicit file_or_directory(directory& d) { 67 | _data.directory = &d; 68 | _is_file = false; 69 | } 70 | bool is_file() const noexcept { 71 | return _is_file; 72 | } 73 | bool is_directory() const noexcept { 74 | return !is_file(); 75 | } 76 | const directory& as_directory() const noexcept { 77 | assert(!is_file()); 78 | return *_data.directory; 79 | } 80 | const file_data& as_file() const noexcept { 81 | assert(is_file()); 82 | return *_data.file_data; 83 | } 84 | }; 85 | 86 | class file_data { 87 | public: 88 | const char* begin_ptr; 89 | const char* end_ptr; 90 | file_data(const file_data&) = delete; 91 | file_data(const char* b, const char* e) : begin_ptr(b), end_ptr(e) {} 92 | }; 93 | 94 | inline std::pair split_path(const std::string& path) { 95 | auto first_sep = path.find("/"); 96 | if (first_sep == path.npos) { 97 | return std::make_pair(path, ""); 98 | } else { 99 | return std::make_pair(path.substr(0, first_sep), path.substr(first_sep + 1)); 100 | } 101 | } 102 | 103 | struct created_subdirectory { 104 | class directory& directory; 105 | class file_or_directory& index_entry; 106 | }; 107 | 108 | class directory { 109 | std::list _files; 110 | std::list _dirs; 111 | std::map _index; 112 | 113 | using base_iterator = std::map::const_iterator; 114 | 115 | public: 116 | 117 | directory() = default; 118 | directory(const directory&) = delete; 119 | 120 | created_subdirectory add_subdir(std::string name) & { 121 | _dirs.emplace_back(); 122 | auto& back = _dirs.back(); 123 | auto& fod = _index.emplace(name, file_or_directory{back}).first->second; 124 | return created_subdirectory{back, fod}; 125 | } 126 | 127 | file_or_directory* add_file(std::string name, const char* begin, const char* end) & { 128 | assert(_index.find(name) == _index.end()); 129 | _files.emplace_back(begin, end); 130 | return &_index.emplace(name, file_or_directory{_files.back()}).first->second; 131 | } 132 | 133 | const file_or_directory* get(const std::string& path) const { 134 | auto pair = split_path(path); 135 | auto child = _index.find(pair.first); 136 | if (child == _index.end()) { 137 | return nullptr; 138 | } 139 | auto& entry = child->second; 140 | if (pair.second.empty()) { 141 | // We're at the end of the path 142 | return &entry; 143 | } 144 | 145 | if (entry.is_file()) { 146 | // We can't traverse into a file. Stop. 147 | return nullptr; 148 | } 149 | // Keep going down 150 | return entry.as_directory().get(pair.second); 151 | } 152 | 153 | class iterator { 154 | base_iterator _base_iter; 155 | base_iterator _end_iter; 156 | public: 157 | using value_type = directory_entry; 158 | using difference_type = std::ptrdiff_t; 159 | using pointer = const value_type*; 160 | using reference = const value_type&; 161 | using iterator_category = std::input_iterator_tag; 162 | 163 | iterator() = default; 164 | explicit iterator(base_iterator iter, base_iterator end) : _base_iter(iter), _end_iter(end) {} 165 | 166 | iterator begin() const noexcept { 167 | return *this; 168 | } 169 | 170 | iterator end() const noexcept { 171 | return iterator(_end_iter, _end_iter); 172 | } 173 | 174 | inline value_type operator*() const noexcept; 175 | 176 | bool operator==(const iterator& rhs) const noexcept { 177 | return _base_iter == rhs._base_iter; 178 | } 179 | 180 | bool operator!=(const iterator& rhs) const noexcept { 181 | return !(*this == rhs); 182 | } 183 | 184 | iterator& operator++() noexcept { 185 | ++_base_iter; 186 | return *this; 187 | } 188 | 189 | iterator operator++(int) noexcept { 190 | auto cp = *this; 191 | ++_base_iter; 192 | return cp; 193 | } 194 | }; 195 | 196 | using const_iterator = iterator; 197 | 198 | iterator begin() const noexcept { 199 | return iterator(_index.begin(), _index.end()); 200 | } 201 | 202 | iterator end() const noexcept { 203 | return iterator(); 204 | } 205 | }; 206 | 207 | inline std::string normalize_path(std::string path) { 208 | while (path.find("/") == 0) { 209 | path.erase(path.begin()); 210 | } 211 | while (!path.empty() && (path.rfind("/") == path.size() - 1)) { 212 | path.pop_back(); 213 | } 214 | auto off = path.npos; 215 | while ((off = path.find("//")) != path.npos) { 216 | path.erase(path.begin() + static_cast(off)); 217 | } 218 | return path; 219 | } 220 | 221 | using index_type = std::map; 222 | 223 | } // detail 224 | 225 | class directory_entry { 226 | std::string _fname; 227 | const detail::file_or_directory* _item; 228 | 229 | public: 230 | directory_entry() = delete; 231 | explicit directory_entry(std::string filename, const detail::file_or_directory& item) 232 | : _fname(filename) 233 | , _item(&item) 234 | {} 235 | 236 | const std::string& filename() const & { 237 | return _fname; 238 | } 239 | std::string filename() const && { 240 | return std::move(_fname); 241 | } 242 | 243 | bool is_file() const { 244 | return _item->is_file(); 245 | } 246 | 247 | bool is_directory() const { 248 | return _item->is_directory(); 249 | } 250 | }; 251 | 252 | directory_entry detail::directory::iterator::operator*() const noexcept { 253 | assert(begin() != end()); 254 | return directory_entry(_base_iter->first, _base_iter->second); 255 | } 256 | 257 | using directory_iterator = detail::directory::iterator; 258 | 259 | class embedded_filesystem { 260 | // Never-null: 261 | const cmrc::detail::index_type* _index; 262 | const detail::file_or_directory* _get(std::string path) const { 263 | path = detail::normalize_path(path); 264 | auto found = _index->find(path); 265 | if (found == _index->end()) { 266 | return nullptr; 267 | } else { 268 | return found->second; 269 | } 270 | } 271 | 272 | public: 273 | explicit embedded_filesystem(const detail::index_type& index) 274 | : _index(&index) 275 | {} 276 | 277 | file open(const std::string& path) const { 278 | auto entry_ptr = _get(path); 279 | if (!entry_ptr || !entry_ptr->is_file()) { 280 | #ifdef CMRC_NO_EXCEPTIONS 281 | fprintf(stderr, "Error no such file or directory: %s\n", path.c_str()); 282 | abort(); 283 | #else 284 | throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path); 285 | #endif 286 | } 287 | auto& dat = entry_ptr->as_file(); 288 | return file{dat.begin_ptr, dat.end_ptr}; 289 | } 290 | 291 | bool is_file(const std::string& path) const noexcept { 292 | auto entry_ptr = _get(path); 293 | return entry_ptr && entry_ptr->is_file(); 294 | } 295 | 296 | bool is_directory(const std::string& path) const noexcept { 297 | auto entry_ptr = _get(path); 298 | return entry_ptr && entry_ptr->is_directory(); 299 | } 300 | 301 | bool exists(const std::string& path) const noexcept { 302 | return !!_get(path); 303 | } 304 | 305 | directory_iterator iterate_directory(const std::string& path) const { 306 | auto entry_ptr = _get(path); 307 | if (!entry_ptr) { 308 | #ifdef CMRC_NO_EXCEPTIONS 309 | fprintf(stderr, "Error no such file or directory: %s\n", path.c_str()); 310 | abort(); 311 | #else 312 | throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path); 313 | #endif 314 | } 315 | if (!entry_ptr->is_directory()) { 316 | #ifdef CMRC_NO_EXCEPTIONS 317 | fprintf(stderr, "Error not a directory: %s\n", path.c_str()); 318 | abort(); 319 | #else 320 | throw std::system_error(make_error_code(std::errc::not_a_directory), path); 321 | #endif 322 | } 323 | return entry_ptr->as_directory().begin(); 324 | } 325 | }; 326 | 327 | } 328 | 329 | #endif // CMRC_CMRC_HPP_INCLUDED 330 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.100" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 10 | 11 | [[package]] 12 | name = "better_embedded" 13 | version = "0.4.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "e28fd9c8a7f31a913128bee48a37e7abc57c37e71d27e8fb41f849cef36f143f" 16 | 17 | [[package]] 18 | name = "bitflags" 19 | version = "2.10.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 22 | 23 | [[package]] 24 | name = "bumpalo" 25 | version = "3.19.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" 28 | 29 | [[package]] 30 | name = "cc" 31 | version = "1.2.50" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" 34 | dependencies = [ 35 | "find-msvc-tools", 36 | "jobserver", 37 | "libc", 38 | "shlex", 39 | ] 40 | 41 | [[package]] 42 | name = "cfg-if" 43 | version = "1.0.4" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 46 | 47 | [[package]] 48 | name = "errno" 49 | version = "0.3.14" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 52 | dependencies = [ 53 | "libc", 54 | "windows-sys", 55 | ] 56 | 57 | [[package]] 58 | name = "fallible-iterator" 59 | version = "0.3.0" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 62 | 63 | [[package]] 64 | name = "fallible-streaming-iterator" 65 | version = "0.1.9" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 68 | 69 | [[package]] 70 | name = "fastrand" 71 | version = "2.3.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 74 | 75 | [[package]] 76 | name = "find-msvc-tools" 77 | version = "0.1.5" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" 80 | 81 | [[package]] 82 | name = "foldhash" 83 | version = "0.2.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" 86 | 87 | [[package]] 88 | name = "getrandom" 89 | version = "0.3.4" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 92 | dependencies = [ 93 | "cfg-if", 94 | "libc", 95 | "r-efi", 96 | "wasip2", 97 | ] 98 | 99 | [[package]] 100 | name = "hashbrown" 101 | version = "0.16.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 104 | dependencies = [ 105 | "foldhash", 106 | ] 107 | 108 | [[package]] 109 | name = "hashlink" 110 | version = "0.11.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" 113 | dependencies = [ 114 | "hashbrown", 115 | ] 116 | 117 | [[package]] 118 | name = "jobserver" 119 | version = "0.1.34" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 122 | dependencies = [ 123 | "getrandom", 124 | "libc", 125 | ] 126 | 127 | [[package]] 128 | name = "js-sys" 129 | version = "0.3.83" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 132 | dependencies = [ 133 | "once_cell", 134 | "wasm-bindgen", 135 | ] 136 | 137 | [[package]] 138 | name = "libc" 139 | version = "0.2.178" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 142 | 143 | [[package]] 144 | name = "libsimple" 145 | version = "0.6.1" 146 | dependencies = [ 147 | "anyhow", 148 | "better_embedded", 149 | "cc", 150 | "rusqlite", 151 | "tempfile", 152 | ] 153 | 154 | [[package]] 155 | name = "libsqlite3-sys" 156 | version = "0.36.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" 159 | dependencies = [ 160 | "cc", 161 | "pkg-config", 162 | "vcpkg", 163 | ] 164 | 165 | [[package]] 166 | name = "linux-raw-sys" 167 | version = "0.11.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 170 | 171 | [[package]] 172 | name = "once_cell" 173 | version = "1.21.3" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 176 | 177 | [[package]] 178 | name = "pkg-config" 179 | version = "0.3.32" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 182 | 183 | [[package]] 184 | name = "proc-macro2" 185 | version = "1.0.103" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 188 | dependencies = [ 189 | "unicode-ident", 190 | ] 191 | 192 | [[package]] 193 | name = "quote" 194 | version = "1.0.42" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 197 | dependencies = [ 198 | "proc-macro2", 199 | ] 200 | 201 | [[package]] 202 | name = "r-efi" 203 | version = "5.3.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 206 | 207 | [[package]] 208 | name = "rusqlite" 209 | version = "0.38.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3" 212 | dependencies = [ 213 | "bitflags", 214 | "fallible-iterator", 215 | "fallible-streaming-iterator", 216 | "hashlink", 217 | "libsqlite3-sys", 218 | "smallvec", 219 | "sqlite-wasm-rs", 220 | ] 221 | 222 | [[package]] 223 | name = "rustix" 224 | version = "1.1.2" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 227 | dependencies = [ 228 | "bitflags", 229 | "errno", 230 | "libc", 231 | "linux-raw-sys", 232 | "windows-sys", 233 | ] 234 | 235 | [[package]] 236 | name = "rustversion" 237 | version = "1.0.22" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 240 | 241 | [[package]] 242 | name = "shlex" 243 | version = "1.3.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 246 | 247 | [[package]] 248 | name = "smallvec" 249 | version = "1.15.1" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 252 | 253 | [[package]] 254 | name = "sqlite-wasm-rs" 255 | version = "0.5.1" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "05e98301bf8b0540c7de45ecd760539b9c62f5772aed172f08efba597c11cd5d" 258 | dependencies = [ 259 | "cc", 260 | "hashbrown", 261 | "js-sys", 262 | "thiserror", 263 | "wasm-bindgen", 264 | ] 265 | 266 | [[package]] 267 | name = "syn" 268 | version = "2.0.111" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 271 | dependencies = [ 272 | "proc-macro2", 273 | "quote", 274 | "unicode-ident", 275 | ] 276 | 277 | [[package]] 278 | name = "tempfile" 279 | version = "3.23.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" 282 | dependencies = [ 283 | "fastrand", 284 | "getrandom", 285 | "once_cell", 286 | "rustix", 287 | "windows-sys", 288 | ] 289 | 290 | [[package]] 291 | name = "thiserror" 292 | version = "2.0.17" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 295 | dependencies = [ 296 | "thiserror-impl", 297 | ] 298 | 299 | [[package]] 300 | name = "thiserror-impl" 301 | version = "2.0.17" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 304 | dependencies = [ 305 | "proc-macro2", 306 | "quote", 307 | "syn", 308 | ] 309 | 310 | [[package]] 311 | name = "unicode-ident" 312 | version = "1.0.22" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 315 | 316 | [[package]] 317 | name = "vcpkg" 318 | version = "0.2.15" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 321 | 322 | [[package]] 323 | name = "wasip2" 324 | version = "1.0.1+wasi-0.2.4" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 327 | dependencies = [ 328 | "wit-bindgen", 329 | ] 330 | 331 | [[package]] 332 | name = "wasm-bindgen" 333 | version = "0.2.106" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 336 | dependencies = [ 337 | "cfg-if", 338 | "once_cell", 339 | "rustversion", 340 | "wasm-bindgen-macro", 341 | "wasm-bindgen-shared", 342 | ] 343 | 344 | [[package]] 345 | name = "wasm-bindgen-macro" 346 | version = "0.2.106" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 349 | dependencies = [ 350 | "quote", 351 | "wasm-bindgen-macro-support", 352 | ] 353 | 354 | [[package]] 355 | name = "wasm-bindgen-macro-support" 356 | version = "0.2.106" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 359 | dependencies = [ 360 | "bumpalo", 361 | "proc-macro2", 362 | "quote", 363 | "syn", 364 | "wasm-bindgen-shared", 365 | ] 366 | 367 | [[package]] 368 | name = "wasm-bindgen-shared" 369 | version = "0.2.106" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 372 | dependencies = [ 373 | "unicode-ident", 374 | ] 375 | 376 | [[package]] 377 | name = "windows-link" 378 | version = "0.2.1" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 381 | 382 | [[package]] 383 | name = "windows-sys" 384 | version = "0.61.2" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 387 | dependencies = [ 388 | "windows-link", 389 | ] 390 | 391 | [[package]] 392 | name = "wit-bindgen" 393 | version = "0.46.0" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 396 | --------------------------------------------------------------------------------