├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yaml │ └── feature_request.md ├── SECURITY.md └── workflows │ ├── release-executable.yaml │ ├── release-npm.yaml │ ├── release-pypi.yaml │ └── scripts │ └── install-rust.sh ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── benchmark.rs ├── build.rs ├── docs ├── BINDINGS_FOR_PYTHON.md ├── executable.md └── functions.md ├── examples └── trial.rs ├── napi ├── DEVELOPMENT.md ├── README.md ├── __test__ │ └── index.spec.mjs ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json └── scripts │ └── runner.js ├── pyproject.toml ├── src ├── elements │ ├── consts.rs │ ├── element.rs │ ├── mod.rs │ ├── tags │ │ ├── general_purpose.rs │ │ ├── heading.rs │ │ ├── link.rs │ │ ├── list.rs │ │ ├── media.rs │ │ ├── mod.rs │ │ ├── preformatted.rs │ │ ├── table.rs │ │ └── text_content.rs │ ├── types.rs │ └── utils.rs ├── lib.rs ├── main.rs ├── nodejs_bindings.rs ├── nodes │ ├── mod.rs │ ├── node.rs │ └── utils.rs ├── python_bindings.rs └── utils │ ├── file.rs │ └── mod.rs └── tests ├── fixtures └── simple-01.html ├── test.rs └── tmp └── .gitkeep /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## 🤝 Code of Conduct 2 | 3 | This project is maintained by volunteers in their personal time. We welcome respectful and constructive participation. Please be kind and patient in all interactions. 4 | 5 | As contributors and maintainers, we aim to: 6 | 7 | - Work together with empathy and cooperation 8 | - Strive to understand each other’s intentions 9 | - Exchange ideas constructively to improve the project 10 | - Treat each other with kindness, especially in disagreement 11 | 12 | Thank you for helping us keep the space friendly and focused. -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## ✨ Contributing 2 | 3 | We’re happy to receive feedback, bug reports, and questions via GitHub Issues. 4 | Pull requests are also welcome — though please note that we may not always be able to accept them. 5 | 6 | This project is maintained as a labor of love. We welcome community participation, but: 7 | 8 | - Issues that are respectful and constructive are appreciated. 9 | - Pull requests are reviewed, but acceptance is not guaranteed. 10 | - We do not engage in long debates or vision disagreements. 11 | - If you have a different direction in mind, please fork freely, provided proper licensing is respected. 12 | 13 | Thanks for understanding the scope and spirit of the project. 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Something isn't working as expected 4 | title: "[Bug] " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Run '...' 17 | 3. See error 18 | 19 | **Expected behavior** 20 | What you expected to happen. 21 | 22 | **Environment (please complete the following information):** 23 | - OS: [e.g. Linux/macOS/Windows] 24 | - Version of this software 25 | - How you started the server (command/flags) 26 | 27 | **Additional context** 28 | Add any other context, logs, or screenshots about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions / Feedback 4 | url: https://github.com/nabbisen/apimock-rs 5 | about: Ask questions or leave general feedback here. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | title: "[Feature] " 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | Any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or use cases for this feature. 21 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | ## 🛡️ Security Policy 2 | 3 | ### Reporting a Vulnerability 4 | 5 | If you discover a potential security issue or vulnerability in this software, **please do not create a public issue**. 6 | 7 | Instead, kindly report it privately by sending an [email 📧](nabbisen@scqr.net]) 8 | 9 | We will do our best to assess and respond in a timely manner, but please understand that this project is maintained voluntarily in free time. Thank you for your understanding and responsible disclosure. 10 | 11 | ### Supported Versions 12 | 13 | This project is currently maintained as a best-effort activity. Only the latest version is considered to receive potential fixes. 14 | -------------------------------------------------------------------------------- /.github/workflows/release-executable.yaml: -------------------------------------------------------------------------------- 1 | name: Executable 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | permissions: 8 | contents: write 9 | 10 | defaults: 11 | run: 12 | shell: bash 13 | 14 | env: 15 | PRODUCT_BASENAME: mdka 16 | TAG: ${{ github.ref_name }} # tag or branch name 17 | JOB_WORKDIR: tmp-${{ github.run_id }} # unique number 18 | 19 | jobs: 20 | build: 21 | runs-on: ${{ matrix.os }} 22 | strategy: 23 | matrix: 24 | include: 25 | - name: Linux-aarch64-musl 26 | target: aarch64-unknown-linux-musl 27 | os: ubuntu-latest 28 | bin_ext: 29 | archive_ext: .tar.gz 30 | - name: Linux-x64-gnu 31 | target: x86_64-unknown-linux-gnu 32 | os: ubuntu-latest 33 | bin_ext: 34 | archive_ext: .tar.gz 35 | - name: Linux-x64-musl 36 | target: x86_64-unknown-linux-musl 37 | os: ubuntu-latest 38 | bin_ext: 39 | archive_ext: .tar.gz 40 | - name: macOS-aarch64 41 | target: aarch64-apple-darwin 42 | os: macos-latest 43 | bin_ext: 44 | archive_ext: .zip 45 | - name: Windows-x64 46 | target: x86_64-pc-windows-msvc 47 | os: windows-latest 48 | bin_ext: .exe 49 | archive_ext: .zip 50 | 51 | steps: 52 | - name: Checkout repository 53 | uses: actions/checkout@v4 54 | 55 | # [ build ] 56 | - name: Install Rust 57 | run: bash .github/workflows/scripts/install-rust.sh stable ${{ matrix.target }} 58 | 59 | - name: Cache cargo dependencies and build 60 | uses: actions/cache@v4 61 | with: 62 | path: | 63 | ~/.cargo/registry 64 | ~/.cargo/git 65 | target 66 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 67 | restore-keys: | 68 | ${{ runner.os }}-cargo- 69 | 70 | - name: Build Rust executable 71 | run: | 72 | cargo build --release --target ${{ matrix.target }} --bin ${{ env.PRODUCT_BASENAME }} --locked 73 | 74 | # [ release asset ] 75 | - name: Prepare for release asset 76 | run: | 77 | BUILT_FILEPATH=target/${{ matrix.target }}/release/${{ env.PRODUCT_BASENAME }}${{ matrix.bin_ext }} 78 | RELEASE_ASSET_BASENAME=${{ env.PRODUCT_BASENAME }}@${{ matrix.name }}-${{ env.TAG }} 79 | RELEASE_SRC_DIR=${RELEASE_ASSET_BASENAME} 80 | RELEASE_ASSET_FILENAME=${RELEASE_ASSET_BASENAME}${{ matrix.archive_ext }} 81 | mkdir -p "${{ env.JOB_WORKDIR }}/${RELEASE_SRC_DIR}" 82 | mv "${BUILT_FILEPATH}" "${{ env.JOB_WORKDIR }}/${RELEASE_SRC_DIR}/" 83 | echo "RELEASE_SRC_DIR=${RELEASE_SRC_DIR}" >> $GITHUB_ENV 84 | echo "RELEASE_ASSET_FILENAME=${RELEASE_ASSET_FILENAME}" >> $GITHUB_ENV 85 | 86 | # BSD tar on macOS: first 8MB of the file are sometimes all NUL byte 87 | # refs: https://github.com/actions/cache/issues/403 , https://github.com/rust-lang/cargo/issues/8603 88 | - name: Mitigate macOS tar bug 89 | if: matrix.target == 'aarch64-apple-darwin' 90 | run: | 91 | sudo /usr/sbin/purge 92 | 93 | - name: Create archive as release asset - Linux 94 | if: > 95 | matrix.target != 'aarch64-apple-darwin' && 96 | matrix.target != 'x86_64-pc-windows-msvc' 97 | run: | 98 | cd "${{ env.JOB_WORKDIR }}" 99 | tar czf "../${RELEASE_ASSET_FILENAME}" "${RELEASE_SRC_DIR}" 100 | 101 | - name: Create archive as release asset - Windows / macOS 102 | if: > 103 | matrix.target == 'aarch64-apple-darwin' || 104 | matrix.target == 'x86_64-pc-windows-msvc' 105 | run: | 106 | cd "${{ env.JOB_WORKDIR }}" 107 | 7z a "../${RELEASE_ASSET_FILENAME}" "${RELEASE_SRC_DIR}" 108 | 109 | - name: Update release with new asset 110 | env: 111 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 112 | run: gh release upload ${{ github.ref_name }} ${RELEASE_ASSET_FILENAME} 113 | -------------------------------------------------------------------------------- /.github/workflows/release-npm.yaml: -------------------------------------------------------------------------------- 1 | name: npm 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | permissions: 8 | contents: write 9 | id-token: write 10 | 11 | defaults: 12 | run: 13 | shell: bash 14 | 15 | env: 16 | PRODUCT_BASENAME: mdka 17 | TAG: ${{ github.ref_name }} # tag or branch name 18 | JOB_WORKDIR: tmp-${{ github.run_id }} # unique number 19 | 20 | jobs: 21 | build: 22 | runs-on: ${{ matrix.os }} 23 | strategy: 24 | matrix: 25 | include: 26 | - name: Linux-x64-gnu 27 | target: x86_64-unknown-linux-gnu 28 | os: ubuntu-latest 29 | archive_ext: .tar.gz 30 | napiplatform: linux-x64-gnu 31 | - name: macOS-aarch64 32 | target: aarch64-apple-darwin 33 | os: macos-latest 34 | archive_ext: .zip 35 | napiplatform: darwin-arm64 36 | - name: Windows-x64 37 | target: x86_64-pc-windows-msvc 38 | os: windows-latest 39 | archive_ext: .zip 40 | napiplatform: win32-x64-msvc 41 | 42 | steps: 43 | - name: Checkout repository 44 | uses: actions/checkout@v4 45 | 46 | # [ Node.js dependencies ] 47 | - name: Prepare napi 48 | run: cp -r napi/* . 49 | # todo: better ? to replace w/ 50 | # working-directory: napi 51 | 52 | - name: Setup Node.js 53 | uses: actions/setup-node@v4 54 | with: 55 | node-version: 22 56 | cache: npm 57 | 58 | - name: Install npm packages 59 | run: npm install 60 | 61 | # [ build ] 62 | - name: Install Rust 63 | run: bash .github/workflows/scripts/install-rust.sh stable ${{ matrix.target }} 64 | 65 | - name: Cache cargo dependencies and build 66 | uses: actions/cache@v4 67 | with: 68 | path: | 69 | ~/.cargo/registry 70 | ~/.cargo/git 71 | target 72 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 73 | restore-keys: | 74 | ${{ runner.os }}-cargo- 75 | 76 | - name: Build Rust executable 77 | run: | 78 | npm run build 79 | 80 | # [ release asset ] 81 | - name: Upload artifact 82 | uses: actions/upload-artifact@v4 83 | with: 84 | name: bindings-${{ matrix.target }} 85 | path: ${{ env.PRODUCT_BASENAME }}.*.node 86 | if-no-files-found: error 87 | 88 | publish: 89 | name: Publish on release tags 90 | needs: 91 | - build 92 | runs-on: ubuntu-latest 93 | steps: 94 | - name: Check if release tag 95 | run: | 96 | if [[ "${TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || \ 97 | [[ "${TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]-rc\.[0-9]+$ ]]; then 98 | echo "${TAG}: Semantic versioning tagged - OK" 99 | else 100 | echo "Not a release tag. Skipped." 101 | exit 1 102 | fi 103 | 104 | - uses: actions/checkout@v4 105 | 106 | - name: Prepare napi 107 | run: cp -r napi/* . 108 | # todo: better ? to replace w/ 109 | # working-directory: napi 110 | 111 | - name: Setup node 112 | uses: actions/setup-node@v4 113 | with: 114 | node-version: 22 115 | cache: npm 116 | 117 | - name: Install dependencies 118 | run: npm install 119 | 120 | - name: Download all artifacts 121 | uses: actions/download-artifact@v4 122 | with: 123 | path: artifacts 124 | 125 | - name: Move artifacts 126 | run: | 127 | npm run create-npm-dir 128 | npm run artifacts 129 | 130 | - name: List packages 131 | run: ls -R ./npm 132 | shell: bash 133 | 134 | - name: Publish 135 | env: 136 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 137 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 138 | run: | 139 | npm config set provenance true 140 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc 141 | npm publish --access public 142 | -------------------------------------------------------------------------------- /.github/workflows/release-pypi.yaml: -------------------------------------------------------------------------------- 1 | # Modified: 2 | # This file is autogenerated by maturin v1.8.2 3 | # To update, run 4 | # 5 | # maturin generate-ci github 6 | # 7 | name: PyPi 8 | 9 | on: 10 | release: 11 | types: [created] 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | linux: 18 | runs-on: ${{ matrix.platform.runner }} 19 | strategy: 20 | matrix: 21 | platform: 22 | - runner: ubuntu-latest 23 | target: x86_64 24 | - runner: ubuntu-latest 25 | target: aarch64 26 | steps: 27 | - uses: actions/checkout@v4 28 | - uses: actions/setup-python@v5 29 | with: 30 | python-version: 3.x 31 | 32 | - name: Cache cargo dependencies and build 33 | uses: actions/cache@v4 34 | with: 35 | path: | 36 | ~/.cargo/registry 37 | ~/.cargo/git 38 | target 39 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-cargo- 42 | 43 | - name: Build wheels 44 | uses: PyO3/maturin-action@v1 45 | with: 46 | target: ${{ matrix.platform.target }} 47 | args: --release --out dist --find-interpreter -F pyo3 48 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 49 | manylinux: auto 50 | - name: Upload wheels 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: wheels-linux-${{ matrix.platform.target }} 54 | path: dist 55 | 56 | musllinux: 57 | runs-on: ${{ matrix.platform.runner }} 58 | strategy: 59 | matrix: 60 | platform: 61 | - runner: ubuntu-latest 62 | target: x86_64 63 | - runner: ubuntu-latest 64 | target: aarch64 65 | steps: 66 | - uses: actions/checkout@v4 67 | - uses: actions/setup-python@v5 68 | with: 69 | python-version: 3.x 70 | 71 | - name: Cache cargo dependencies and build 72 | uses: actions/cache@v4 73 | with: 74 | path: | 75 | ~/.cargo/registry 76 | ~/.cargo/git 77 | target 78 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 79 | restore-keys: | 80 | ${{ runner.os }}-cargo- 81 | 82 | - name: Build wheels 83 | uses: PyO3/maturin-action@v1 84 | with: 85 | target: ${{ matrix.platform.target }} 86 | args: --release --out dist --find-interpreter -F pyo3 87 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 88 | manylinux: musllinux_1_2 89 | - name: Upload wheels 90 | uses: actions/upload-artifact@v4 91 | with: 92 | name: wheels-musllinux-${{ matrix.platform.target }} 93 | path: dist 94 | 95 | windows: 96 | runs-on: ${{ matrix.platform.runner }} 97 | strategy: 98 | matrix: 99 | platform: 100 | - runner: windows-latest 101 | target: x64 102 | steps: 103 | - uses: actions/checkout@v4 104 | - uses: actions/setup-python@v5 105 | with: 106 | python-version: 3.x 107 | architecture: ${{ matrix.platform.target }} 108 | 109 | - name: Cache cargo dependencies and build 110 | uses: actions/cache@v4 111 | with: 112 | path: | 113 | ~/.cargo/registry 114 | ~/.cargo/git 115 | target 116 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 117 | restore-keys: | 118 | ${{ runner.os }}-cargo- 119 | 120 | - name: Build wheels 121 | uses: PyO3/maturin-action@v1 122 | with: 123 | target: ${{ matrix.platform.target }} 124 | args: --release --out dist --find-interpreter -F pyo3 125 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 126 | - name: Upload wheels 127 | uses: actions/upload-artifact@v4 128 | with: 129 | name: wheels-windows-${{ matrix.platform.target }} 130 | path: dist 131 | 132 | macos: 133 | runs-on: ${{ matrix.platform.runner }} 134 | strategy: 135 | matrix: 136 | platform: 137 | - runner: macos-latest 138 | target: aarch64 139 | steps: 140 | - uses: actions/checkout@v4 141 | - uses: actions/setup-python@v5 142 | with: 143 | python-version: 3.x 144 | 145 | - name: Cache cargo dependencies and build 146 | uses: actions/cache@v4 147 | with: 148 | path: | 149 | ~/.cargo/registry 150 | ~/.cargo/git 151 | target 152 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 153 | restore-keys: | 154 | ${{ runner.os }}-cargo- 155 | 156 | - name: Build wheels 157 | uses: PyO3/maturin-action@v1 158 | with: 159 | target: ${{ matrix.platform.target }} 160 | args: --release --out dist --find-interpreter -F pyo3 161 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 162 | - name: Upload wheels 163 | uses: actions/upload-artifact@v4 164 | with: 165 | name: wheels-macos-${{ matrix.platform.target }} 166 | path: dist 167 | 168 | sdist: 169 | runs-on: ubuntu-latest 170 | steps: 171 | - uses: actions/checkout@v4 172 | - name: Build sdist 173 | uses: PyO3/maturin-action@v1 174 | with: 175 | command: sdist 176 | args: --out dist 177 | - name: Upload sdist 178 | uses: actions/upload-artifact@v4 179 | with: 180 | name: wheels-sdist 181 | path: dist 182 | 183 | release: 184 | name: Release 185 | runs-on: ubuntu-latest 186 | if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} 187 | needs: [linux, musllinux, windows, macos, sdist] 188 | permissions: 189 | # Use to sign the release artifacts 190 | id-token: write 191 | # Used to upload release artifacts 192 | contents: write 193 | # Used to generate artifact attestation 194 | attestations: write 195 | steps: 196 | - uses: actions/download-artifact@v4 197 | - name: Generate artifact attestation 198 | uses: actions/attest-build-provenance@v1 199 | with: 200 | subject-path: 'wheels-*/*' 201 | - name: Publish to PyPI 202 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 203 | uses: PyO3/maturin-action@v1 204 | env: 205 | MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} 206 | with: 207 | command: upload 208 | args: --non-interactive --skip-existing wheels-*/* 209 | -------------------------------------------------------------------------------- /.github/workflows/scripts/install-rust.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Install/update rust. 3 | # The first argument should be the toolchain to install. 4 | 5 | set -ex 6 | if [ -z "$1" ] 7 | then 8 | echo "First parameter must be toolchain to install." 9 | exit 1 10 | fi 11 | TOOLCHAIN="$1" 12 | 13 | rustup set profile minimal 14 | rustup component remove --toolchain=$TOOLCHAIN rust-docs || echo "already removed" 15 | rustup update --no-self-update $TOOLCHAIN 16 | if [ -n "$2" ] 17 | then 18 | TARGET="$2" 19 | HOST=$(rustc -Vv | grep ^host: | sed -e "s/host: //g") 20 | if [ "$HOST" != "$TARGET" ] 21 | then 22 | rustup component add llvm-tools-preview --toolchain=$TOOLCHAIN 23 | rustup component add rust-std-$TARGET --toolchain=$TOOLCHAIN 24 | fi 25 | if [[ $TARGET == *"musl" ]] 26 | then 27 | # This is needed by libdbus-sys. 28 | sudo apt update -y && sudo apt install musl-dev musl-tools -y 29 | fi 30 | if [[ $TARGET == "aarch64-unknown-linux-musl" ]] 31 | then 32 | echo CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=rust-lld >> $GITHUB_ENV 33 | # This `CC` is some nonsense needed for libdbus-sys (via opener). 34 | # I don't know if this is really the right thing to do, but it seems to work. 35 | sudo apt install gcc-aarch64-linux-gnu -y 36 | echo CC=aarch64-linux-gnu-gcc >> $GITHUB_ENV 37 | fi 38 | fi 39 | 40 | rustup default $TOOLCHAIN 41 | rustup -V 42 | rustc -Vv 43 | cargo -V -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | # Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # custom 17 | tests/tmp/* 18 | !tests/tmp/.gitkeep 19 | 20 | # napi 21 | node_modules/ 22 | *.node 23 | bun.lock 24 | bun.lockb 25 | .env 26 | .env.* 27 | !.env.example 28 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "rust-lang.rust-analyzer" 4 | ] 5 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mdka" 3 | version = "1.5.0" 4 | edition = "2021" 5 | 6 | description = "HTML to Markdown converter" 7 | authors = ["nabbisen "] 8 | license = "Apache-2.0" 9 | categories = ["development-tools", "parsing", "parser-implementations"] 10 | keywords = ["html", "markdown", "parser", "conversion"] 11 | repository = "https://github.com/nabbisen/mdka-rs" 12 | readme = "README.md" 13 | 14 | rust-version = "1.74.0" 15 | 16 | [lib] 17 | name = "mdka" 18 | crate-type = ["rlib", "cdylib"] 19 | 20 | [profile.release] # shrink executable size 21 | opt-level = "z" 22 | lto = true 23 | # panic = "abort" 24 | strip = true 25 | codegen-units = 1 26 | 27 | [profile.dev] # to reasonably improve productivity 28 | opt-level = 1 # slightly optimize 29 | lto = false # disable link-time optimizations 30 | # strip = "debuginfo" # reduce size 31 | incremental = true 32 | 33 | [features] 34 | default = [] 35 | pyo3 = ["dep:pyo3"] 36 | napi = ["dep:napi", "dep:napi-derive", "dep:napi-build"] 37 | 38 | [dependencies] 39 | html5ever = "^0.27" 40 | markup5ever_rcdom = "^0.3" 41 | # binding for python 42 | pyo3 = { version = "0", features = ["extension-module"], optional = true } 43 | # binding for node.js 44 | napi = { version = "2", default-features = false, features = ["napi5", "async"], optional = true } 45 | napi-derive = { version = "2", optional = true } 46 | 47 | [dev-dependencies] 48 | criterion = "0" 49 | maturin = "1" 50 | 51 | [build-dependencies] 52 | napi-build = { version = "2", optional = true } 53 | 54 | [[example]] 55 | name = "trial" 56 | path = "examples/trial.rs" 57 | 58 | [[bench]] 59 | name = "benchmark" 60 | harness = false 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mdka 2 | 3 | **HTML to Markdown (MD)** converter written in [Rust](https://www.rust-lang.org/). 4 | 5 | [![crates.io](https://img.shields.io/crates/v/mdka?label=latest)](https://crates.io/crates/mdka) 6 | [![Documentation](https://docs.rs/mdka/badge.svg?version=latest)](https://docs.rs/mdka) 7 | [![Dependency Status](https://deps.rs/crate/mdka/latest/status.svg)](https://deps.rs/crate/mdka) 8 | [![Executable](https://github.com/nabbisen/mdka-rs/actions/workflows/release-executable.yaml/badge.svg)](https://github.com/nabbisen/mdka-rs/actions/workflows/release-executable.yaml) 9 | [![PyPi](https://github.com/nabbisen/mdka-rs/actions/workflows/release-pypi.yaml/badge.svg)](https://github.com/nabbisen/mdka-rs/actions/workflows/release-pypi.yaml) 10 | [![License](https://img.shields.io/github/license/nabbisen/mdka-rs)](https://github.com/nabbisen/mdka-rs/blob/main/LICENSE) 11 | 12 | ## Summary 13 | 14 | A kind of text manipulator named mdka. "ka" means "化 (か)" pointing to conversion. 15 | Designed with in mind: 16 | 17 | - Fast speed 18 | - Low memory consumption 19 | - Easy usage 20 | 21 | ## Usage 22 | 23 | ### 🌠 Rust with cargo 24 | 25 | ```toml 26 | # Cargo.toml 27 | [dependencies] 28 | mdka = "1" 29 | ``` 30 | 31 | ```rust 32 | // awesome.rs 33 | use mdka::from_html 34 | 35 | fn awesome_fn() { 36 | let input = r#" 37 |

heading 1

38 |

Hello, world.

"#; 39 | let ret = from_html(input); 40 | println!("{}", ret); 41 | // # heading 1 42 | // 43 | // Hello, world. 44 | // 45 | } 46 | ``` 47 | 48 | For more details about functions, check out [the docs](docs/functions.md). 49 | 50 | ### 🧩 Executable 51 | 52 | [**Assets**](https://github.com/nabbisen/mdka-rs/releases/latest) in Releases offer executables for multiple platforms → [For usage](docs/executable.md). 53 | 54 | ### 🧩 Python integration 55 | 56 | Bindings for Python are supported → [For more examples](docs/BINDINGS_FOR_PYTHON.md#usage) | [PyPi](https://pypi.org/project/mdka/). 57 | 58 | ```console 59 | $ pip install mdka 60 | ``` 61 | 62 | ```python 63 | # awesome.py 64 | from mdka import md_from_html 65 | 66 | print(md_from_html("

Hello, world.

")) 67 | # Hello, world. 68 | # 69 | ``` 70 | 71 | ### 🧩 Node.js integration 72 | 73 | Bindings for Node.js are supported → [For more examples](napi/README.md#usage) | [npm](https://www.npmjs.com/package/mdka). 74 | 75 | ```console 76 | $ npm install mdka 77 | ``` 78 | 79 | ```js 80 | // awesome.js 81 | const { fromHtml } = require("mdka") 82 | 83 | console.log(fromHtml("

Hello, world.

")) 84 | // Hello, world. 85 | // 86 | ``` 87 | 88 | --- 89 | 90 | ## Open-source, with care 91 | 92 | This project is lovingly built and maintained by volunteers. 93 | We hope it helps streamline your work. 94 | Please understand that the project has its own direction — while we welcome feedback, it might not fit every edge case 🌱 95 | 96 | ## Acknowledgements 97 | 98 | Depends on Servo's [html5ever](https://github.com/servo/html5ever) / markup5ever. 99 | Also, on PyO3's [pyo3](https://github.com/PyO3/pyo3) / [maturin](https://github.com/PyO3/maturin) on bindings for Python. [napi-rs](https://github.com/napi-rs/napi-rs) for binding for Node.js. 100 | -------------------------------------------------------------------------------- /benches/benchmark.rs: -------------------------------------------------------------------------------- 1 | use std::hint::black_box; 2 | 3 | /// 4 | /// usage: 5 | /// 6 | /// ``` 7 | /// cargo bench 8 | /// ``` 9 | /// 10 | /// will be generated: target/criterion/report/index.html 11 | /// 12 | use criterion::{criterion_group, criterion_main, Criterion}; 13 | 14 | use mdka::from_html; 15 | 16 | const BENCHMARK_NAME: &str = "mdka"; 17 | const BENCHMARK_INPUT: &str = r#" 18 |

Heading 1

19 | 20 |

ヘディング 2

21 |

こんにちは、世界。

22 | 23 |
Lorem, ipsum, dolor.
24 | 25 | 33 | 34 |
    35 |
  1. ١ 36 |
  2. ٢ 37 |
  3. ٣ 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
٤٥٦
٧٨٩
60 | 61 |

62 | println!(\"Hello, world.\")
63 | 
64 | 65 |
66 | Lorem, ipsum, dolor.
67 | Lorem, ipsum, dolor. 68 |
69 | 70 |
71 | 72 | something 73 | 74 | great-caption 75 |